Writing Test Cases
A test case is a contract between you and the future. Someone who has never seen the feature — including you in six months — must be able to execute it and reach the same conclusion. Writing clear, reproducible test cases is one of the most fundamental QA skills, and it separates reliable test suites from documentation that gathers dust.
Anatomy of a Good Test Case
Every test case, regardless of your tooling (TestRail, Zephyr, Google Sheets, or plain Markdown), should include these fields:
| Field | Purpose | Example |
|---|---|---|
| ID | Unique reference for traceability | TC-LOGIN-004 |
| Title | One-line summary of what is being verified | Verify login fails with expired password |
| Preconditions | State the system must be in before starting | User account exists with password expired 1 day ago |
| Steps | Numbered, atomic actions | 1. Navigate to /login 2. Enter email 3. Enter expired password 4. Click Submit |
| Expected Result | Observable, measurable outcome | Error message "Your password has expired" is displayed; user is not redirected to dashboard |
| Actual Result | Filled during execution | (blank until run) |
| Test Data | Specific values needed | Email: expired@test.com, Password: OldPass123! |
| Priority | Execution order importance | High |
The ID Convention
Use a consistent naming pattern across your project. Common formats:
TC-MODULE-NNN(e.g., TC-LOGIN-004, TC-CART-012)FEATURE.SCENARIO.NNN(e.g., AUTH.EXPIRED_PW.001)- Auto-incrementing numeric IDs from your test management tool
The format matters less than consistency. Pick one and enforce it.
Title Best Practices
The title should answer "what is being verified?" in one line. Start with a verb:
- Good: "Verify login fails with expired password"
- Good: "Confirm item quantity updates in cart after increment"
- Bad: "Login test" (too vague)
- Bad: "Test that when a user who has an expired password tries to log in the system shows an error" (too long)
Common Mistakes
These are the most frequent problems that make test cases unreliable:
1. Vague Steps
Bad: "Log in to the app"
Which user? Which environment? What credentials? A new team member cannot execute this.
Good: "Navigate to https://staging.example.com/login. Enter email: test@example.com. Enter password: Test123!. Click the 'Sign In' button."
2. Missing Preconditions
The test assumes a certain database state that nobody sets up. For example, testing that a discount code works — but the code was created manually in staging last month and has since been deleted.
Fix: State every precondition explicitly. If test data is required, link to the setup script or describe the creation steps.
3. Compound Assertions
Checking five things in one test case makes failure ambiguous. If the test fails, which of the five checks was the problem?
Bad: One test case that verifies login, navigation to dashboard, profile display, notification count, and logout.
Good: Separate test cases for each verification, sharing preconditions where appropriate.
4. Platform Assumptions
"Click the button" — on mobile there is no click, there is a tap. "Right-click to open context menu" — does not work on touch devices.
Fix: Specify the platform and interaction mode, or write platform-specific variants.
5. Missing Negative Cases
Only testing the happy path. You wrote "Verify login succeeds with valid credentials" but never wrote "Verify login fails with empty password."
Fix: For every positive test case, ask yourself: what should happen when the user does this wrong?
The Given/When/Then Template
GIVEN [precondition / system state]
WHEN [user action or trigger]
THEN [expected observable result]
This format, originating from BDD (Behavior-Driven Development), works even outside Cucumber or Gherkin frameworks. It forces you to separate setup from action from assertion.
Examples
GIVEN a registered user with an active account
WHEN the user enters valid email and password and clicks Sign In
THEN the user is redirected to /dashboard and sees a welcome message
GIVEN a shopping cart with 2 items totaling $50.00
WHEN the user applies discount code "SAVE10" (10% off)
THEN the cart total updates to $45.00 and the discount line is visible
GIVEN a user with no upload permissions
WHEN the user attempts to upload a file via the API
THEN the API returns HTTP 403 with message "Insufficient permissions"
When to Use Given/When/Then vs Traditional Format
| Scenario | Recommended Format |
|---|---|
| Agile team using BDD / Cucumber | Given/When/Then (mandatory) |
| Quick internal test documentation | Given/When/Then (concise) |
| Formal test plans for regulated industries | Traditional table format (detailed) |
| Exploratory testing charters | Neither — use free-form charters |
Test Case Maintenance
Writing test cases is only half the job. Maintaining them is the other half.
Signs Your Test Cases Need Updating
- The feature was redesigned but the test cases still reference the old UI
- Steps reference environments or URLs that no longer exist
- Test data (specific user accounts, product IDs) has been cleaned up
- New edge cases were discovered in production but never added to the suite
Maintenance Practices
- Review test cases during sprint refinement — if a story modifies an existing feature, flag the related test cases for update.
- Tag test cases with feature areas — makes it easy to find all tests related to "checkout" or "user management."
- Archive, do not delete — if a feature is removed, move the test cases to an archive folder rather than deleting them. They may be useful for regression if the feature returns.
- Version your test cases — if using files (Markdown, spreadsheets), keep them in source control. If using a tool, use the tool's versioning feature.
Practical Exercise
Write test cases for a password reset feature with these requirements:
- User clicks "Forgot Password" on the login page
- User enters their email address
- System sends a reset link valid for 24 hours
- User clicks the link, enters a new password (minimum 8 characters, at least one number)
- System confirms the password has been reset
Try writing at least six test cases: three positive (happy path, edge of valid input) and three negative (expired link, invalid password format, non-existent email).
Use the Given/When/Then format for each.
Key Takeaways
- A test case must be executable by someone who has never seen the feature
- Include ID, title, preconditions, steps, expected result, and test data
- Use Given/When/Then to enforce clarity and separation of concerns
- Avoid vague steps, compound assertions, and missing negative cases
- Maintain test cases as actively as you maintain code