Testing case sensitivity is not about finding every possible combination of uppercase and lowercase. It is about finding the boundaries of the case policy and verifying that every code path enforces that policy consistently.
Step 1: Identify the Fields
Not all fields need case-sensitivity testing. Focus on fields that:
- Are compared for equality (authentication, lookup, uniqueness checks)
- Have a security function (tokens, codes, keys)
- Are displayed to users after storage (display consistency)
- Feed into another system's comparison (cross-service usernames, IDs)
Fields that are purely stored and displayed without comparison (a bio, a product description, a note) have lower case-sensitivity risk — the main question is only whether storage preserves the original case faithfully.
Step 2: Define the Expected Policy
For each field, determine the intended policy:
- Case-sensitive:
Adminandadminare different values - Case-insensitive, normalize on input: all values are stored in a canonical case (lowercase or uppercase); comparisons use the stored canonical form
- Case-insensitive, normalize on comparison: values are stored as-submitted; comparisons always normalize both sides
Document this policy explicitly before testing. Without it, you cannot determine whether a behavior is a bug or a feature.
Step 3: Build the Test Matrix
For a case-insensitive username field that normalizes to lowercase on input:
| Test | Input | Expected stored | Expected login | Notes |
|---|---|---|---|---|
| All lowercase | admin |
admin |
✓ with admin |
Baseline |
| All uppercase | ADMIN |
admin |
✓ with ADMIN, admin |
Normalized on storage |
| Mixed case | Admin |
admin |
✓ with Admin, admin |
Normalized on storage |
| Mixed case | aDmIn |
admin |
✓ with aDmIn, admin |
Normalized on storage |
| With non-ASCII | Ädmin |
depends | need spec | Is Ä normalized to ä? |
For a case-sensitive password field:
| Test | Input | Expected stored | Expected auth | Notes |
|---|---|---|---|---|
| Exact case | P@ssw0rd |
hashed | ✓ with P@ssw0rd |
Baseline |
| All lowercase | p@ssw0rd |
— | ✗ | Different password |
| All uppercase | P@SSW0RD |
— | ✗ | Different password |
| One char changed | P@ssW0rd |
— | ✗ | Case difference matters |
Step 4: Test Every Code Path
For each field in the matrix, test every code path that accepts the same value:
- Registration / creation endpoint
- Login / authentication endpoint
- Password reset / account recovery
- Profile lookup / search
- Admin panel search and management
- API endpoints (may have separate implementations)
- Email-triggered flows (confirmation, notification)
The goal: verify that all paths apply the same case policy. A single inconsistent path is a bug.
Step 5: Test Cross-System Boundaries
If the application integrates with external systems, also test:
- SSO / OAuth: does the external provider normalize case? Does your application handle mismatches?
- Exported data (CSVs, reports): is case preserved or normalized?
- Imported data: if you import a user with
ADMIN, what case is stored? - Webhooks and event payloads: does the receiving system compare case correctly?
Common Findings
The most common case-sensitivity bugs found in real applications:
- Inconsistent normalization: Registration lowercases, login does not — users who registered with uppercase letters cannot log in
- Case-sensitive UNIQUE constraint: Allows both
adminandAdminto be registered — different records exist for visually identical usernames - Token case-insensitivity: A password reset token matches even with wrong case — reduced security
- Search doesn't find all records: Case-sensitive search in a case-insensitive application — admin cannot find a user by typing their name in a different case
- Display inconsistency: Username stored as
adminbut displayed as the originally submittedAdminbecause the display path reads from a different source than the storage path