The single best test of a test case is whether someone who has never seen the feature can execute it on the first try without asking questions. Testers who only run their own test cases never feel the pain of vague writing — they fill the gaps from memory. The moment another tester picks up the case (or you re-read your own work three months later), the gaps become bugs in the documentation. This lesson is about writing test cases that survive contact with someone who is not you.
The structure that always works
A useful test case has seven sections, in this order:
- ID — a stable identifier (TC-101) that bug tickets and reports can reference.
- Title — what is being verified, in one line. "Login with valid credentials redirects to dashboard."
- Preconditions — the state the system must be in before the steps run. "User account exists with email
test@example.comand verified status." - Steps — numbered actions, in plain English, each one specific and unambiguous.
- Expected result — what should happen if the test passes. Concrete, testable, observable.
- Actual result — left blank when writing the case; filled in during execution.
- Status — pass, fail, blocked, not run.
That is it. There is no need for elaborate templates. The seven sections give a developer or a fellow tester everything they need to run, fail, or pass the test deterministically.
Specificity is the whole game
The single biggest gap between a poorly written test case and a well-written one is specificity. Compare two ways of writing the same step:
- ❌ "Add the item to the cart."
- ✅ "On the product page for
Acme Wireless Headphones, click the 'Add to cart' button."
The vague version assumes you know which item, where the button is, and what "add" really means. The specific version names the page, the product, and the button by its visible label. Two different testers running the specific version will perform the same action; two testers running the vague version will not.
The same principle applies to expected results:
- ❌ "Order is placed successfully."
- ✅ "Order confirmation page displays with: order ID (format
ORD-followed by 6 digits), order total, expected delivery date, and a 'View order' button. A confirmation email is queued in SendGrid sandbox within 30 seconds."
The vague version is a wish; the specific version is a contract. If the order ID is missing, the test fails. If the email takes 5 minutes, the test fails. The specific version makes failure observable; the vague version invites two testers to disagree on whether the result was right.
Side by side
The same test case — poorly written vs well written
❌ Poorly written
Title: "Login bug"
Title gives no clue what's being tested or what would constitute pass/fail.
Preconditions: "Have an account."
Which account? Verified or not? In what state? Two testers run this with different setups.
Step 1: "Open the site and try to log in."
Which URL? Which credentials? "Try" — what action exactly?
Expected: "Should work."
Work how? What screen? Two testers will disagree on whether this passed.
✅ Well written
Title: "Login with valid credentials redirects to dashboard"
States the feature, the condition, and the expected outcome — readable in one line.
Preconditions: account `test@example.com`, verified, default plan.
Specific account, specific state. Anyone can recreate it.
Step 1: "Open https://staging.acme.com/login. Enter test@example.com and password Sample123. Click 'Sign in'."
Specific URL, specific values, specific button. No ambiguity.
Expected: "Browser navigates to /dashboard within 2s. Welcome banner shows the user's first name. Top-nav shows 'Sign out' button."
Three concrete, observable outcomes. Failure is unambiguous.
The right-hand version takes maybe 90 seconds longer to write. It saves hours over the case's lifetime in clarification cycles, ambiguous failures, and onboarding new testers.
Positive and negative cases
For every feature, you need both kinds of test case. Positive cases verify the system does what it is supposed to do when given valid input — the happy path. Negative cases verify the system handles invalid input gracefully — wrong password, empty fields, expired tokens, malformed dates. A test plan that has only positive cases certifies "the feature works for users who do everything right" — which is roughly half the work. Real users do not always do everything right, and the bugs that hit production almost always live in the cases nobody wrote.
For a login feature, the minimum positive/negative coverage is:
- ✅ Valid login — correct email and password, account verified. Expected: redirect to dashboard.
- ❌ Invalid password — correct email, wrong password. Expected: stay on login page, show "Invalid credentials" error within 200ms, no token issued.
- ❌ Empty fields — submit with email or password blank. Expected: client-side validation message under each empty field, no network request fired.
Three cases. Two are negative. Together they cover the realistic shape of how users interact with a login form, not just the ideal one.
Finding the right level of detail
Too much detail makes cases brittle (every UI change breaks half the suite); too little makes them unrunnable by anyone but the author. A practical rule: write at the level a new tester on day three could execute. They know the product names and main areas, but not the dev quirks or "obvious" assumptions. Cases at this level age well and onboard new testers efficiently. The test case writing cheat sheet has a printable template.
⚠️ Common mistakes
- Writing for yourself. A test case you can run from memory is not a test case. Hand it to a colleague who has never seen the feature; if they need to ask one question, the case is not finished.
- Skipping negative cases. "Testing complete" with only happy-path cases means half the bugs are still in production waiting to be discovered. Always write at least as many negative cases as positive ones for any user-facing form.
- Hard-coding throwaway data into steps. "Use account
vimal-personal-account" works once and breaks when that account is deleted. Use stable, agreed-upon test accounts and reference them by purpose ("the verified default-plan account").
🎯 Practice task
Spend 25 minutes upgrading test cases on a feature you know.
- Pick a feature with at least three user-facing inputs — a sign-up form, a checkout, a settings page.
- Write three test cases using the seven-section structure: one positive (happy path) and two negative (an obvious invalid input and a non-obvious one like a special-character edge case).
- Hand one of them to a colleague (or a friend) and ask them to execute it. Note any clarification they need before they can complete the steps. Each clarification is a gap in your writing — fix it.
- Save the three cases in your team's test management tool or a shared doc. Reference them by ID in any future bug ticket that exercises the same area, so the loop from "case → bug → fix → re-run case" is auditable.