How to Write Test Cases (With a Template and Examples)
A good test case is a small, repeatable experiment: given this state, do these steps, expect exactly this result. Here is the standard anatomy aligned with ISO/IEC/IEEE 29119 and ISTQB, a fill-in template, and worked examples.
Most testing advice treats a test case like paperwork — a box QA has to fill so a spreadsheet looks complete. That framing is why so many test cases are useless. A test case is not documentation; it is a small, repeatable experiment. It says: put the system in this state, do these things, and you should see exactly this. If you see something else, the experiment failed and you have found a bug. Everything good about a test case follows from taking that experiment framing seriously.
Two things flow from it. First, repeatability: the same case run twice, by two different people, on two different days, should reach the same verdict. Second, hand-off: the person who executes a case is often not the person who wrote it, so the case has to carry all the context in the text — not in the author's head. This guide walks through the standard anatomy of a test case (aligned with the ISO/IEC/IEEE 29119 series and ISTQB terminology), gives you a template you can copy, shows two fully worked examples, and names the mistakes that quietly wreck a test suite.
Why well-written test cases matter
A test case is a set of preconditions, inputs, and actions with an expected result, written to verify one specific behavior of the system under test. Well-written cases are repeatable, so anyone can execute them and reach the same pass or fail verdict; they give coverage you can measure, and they let one tester hand work to another without loss.
Coverage is the first payoff. When behaviors are written as discrete cases, you can count them, map them to requirements, and see the gaps — the paths nobody is checking. A vague plan that says "test login" hides the fact that nobody ever tried a locked account or an expired session. A list of cases makes the missing ones visible.
Regression protection is the second. The bug you fixed last month comes back; a saved test case is the trap that catches it before your users do. And the third, quietly the most valuable, is hand-off. Testers rotate, contractors leave, and six months from now someone reads your case cold. If it only works when you run it, it isn't a test case — it's a personal ritual. The whole point of the structured fields below is to move the knowledge out of your head and into text that survives you.
The anatomy of a good test case
Both the ISO/IEC/IEEE 29119 test documentation standard and the ISTQB glossary describe the same core fields. Formats differ across tools, but the semantics are stable. Here is each field, what it means, and the trap to avoid.
| Field | What it means / example |
|---|---|
| Test Case ID | A unique, stable identifier so the case can be referenced, traced, and reported on. Example: TC-LOGIN-001. |
| Title / Objective | One line naming the single behavior under test. Example: "User logs in with valid credentials." If you need the word "and," you probably have two cases. |
| Preconditions | The required state before step 1: the environment, the test data, and the state of the item under test. Example: "A verified account exists (user@example.com / correct password); the user is signed out; the login page is reachable." |
| Test Steps | The numbered actions to perform, each concrete enough to execute without guessing. Example: 1) Open /login. 2) Enter the email. 3) Enter the password. 4) Click "Sign in." |
| Test Data | The specific inputs used, separated from the steps so the same steps can be re-run with different data. Example: email user@example.com, password Correct-Horse-9. |
| Expected Result — the one that matters most | Exactly one observable outcome, stated so precisely that pass or fail needs no judgment. Not "login works" but "the user lands on /dashboard, sees their name in the header, and no error is shown." This is the field testers most often write vaguely, and vagueness here poisons the whole case. |
| Actual Result | What actually happened when the case was executed. Filled at run time. When it differs from expected, this becomes your bug evidence. |
| Status | Pass or Fail (some teams add Blocked / Not Run). Determined by comparing actual against expected — nothing else. |
| Priority | How important this behavior is (e.g. High / Medium / Low), used to decide execution order under time pressure. A valid-login case is High; an obscure edge case may be Low. |
A worked example: login, positive and negative
Abstract fields only click when you see them filled in. Here are two cases for the same feature — one positive (valid credentials should succeed) and one negative (wrong password should fail gracefully) — written the way you would actually hand them to another tester.
| Field | TC-LOGIN-001 (positive) | TC-LOGIN-002 (negative) |
|---|---|---|
| Title | User logs in with valid credentials | Login is rejected with a wrong password |
| Priority | High | High |
| Preconditions | Verified account exists; user is signed out; /login is reachable | Same verified account exists; user is signed out; /login is reachable |
| Test Data | user@example.com / Correct-Horse-9 | user@example.com / Wrong-Password-1 |
| Steps | 1. Open /login 2. Enter the email 3. Enter the password 4. Click "Sign in" | 1. Open /login 2. Enter the email 3. Enter the wrong password 4. Click "Sign in" |
| Expected Result | User is redirected to /dashboard within 2s; their display name appears in the header; no error is shown; a session cookie is set | User stays on /login; an inline error "Email or password is incorrect" is shown; no session cookie is set; the password field is cleared |
| Actual Result | filled at run time | filled at run time |
| Status | Pass / Fail | Pass / Fail |
Notice how much is nailed down. The negative case does not just say "login fails" — it specifies how it should fail: the same page, a specific error message, no session cookie, and a cleared password field. That precision is what turns a fuzzy "it errored" into a gradeable outcome. A tester who has never seen this app can run either case and produce the same verdict you would, which is exactly the repeatability and hand-off property we started with.
Positive, negative, and edge cases
Good coverage means testing more than the happy path. Three flavors do most of the work:
- Positive cases confirm the system does what it should on valid input and supported paths — the login that succeeds, the form that saves, the payment that clears.
- Negative cases confirm the system fails gracefully on invalid input — wrong password, malformed email, expired token — rejecting the input and showing the right error instead of crashing or silently allowing it.
- Edge cases probe boundaries: the empty field, the maximum-length string, zero, the exact limit value, the leap-second, the duplicate submit. Bugs cluster at boundaries, which is why the 29119-4 test design techniques (like boundary value analysis and equivalence partitioning) exist to derive them systematically rather than by gut feel.
A feature is not really covered until it has all three. Most teams over-invest in positive cases and under-invest in the negative and edge ones — which is precisely where the escaped bugs live.
Common mistakes that wreck test cases
The unifying theme is that all five mistakes move knowledge out of the written case and into the author's memory — which defeats the entire purpose. A case that only you can execute is not a test case; it is a note to yourself. Write for the stranger who will run it after you have moved teams.
Best practices
Five habits separate durable test suites from decaying ones:
- One behavior per case. If the title needs "and," split it. Atomic cases give precise failure signals and let you re-run only what broke.
- Atomic and independent. A case must not depend on another case having run first. Set up its own state in preconditions; clean up after itself. Independent cases can run in any order, in parallel, and in isolation.
- Executable by someone else. The acid test: hand it to a colleague who has never seen the feature. If they can run it without asking you a question, it is written well.
- Traceable to acceptance criteria. Every case should answer "which requirement does this verify?" This is where testing meets the spec: cases derive from acceptance criteria, so if you write sharp criteria up front, the cases almost fall out of them. See our guide on writing acceptance criteria that prevent bugs for the front half of this pipeline.
- Keep them alive. A test case is a living asset. When the feature changes, the case changes with it; a suite full of stale cases is worse than no suite, because it manufactures false confidence.
When a case fails: make the actual result real
Here is the moment that connects test cases to everything downstream. A case fails — actual result does not match expected — and now a developer has to fix it. The single biggest determinant of how fast that fix happens is the quality of the Actual Result field. And most testers fill it in the weakest possible way: a sentence typed from memory. "Got an error on submit." Which error? What was on the network? What state was the app in? The developer is now debugging your prose instead of the bug.
The fix is to make the Actual Result a captured artifact, not a recollection. Instead of describing what you saw, attach the reproduction itself: a session replay of the exact steps, the console log at the moment it broke, and the network requests that were in flight. That is precisely what BugMojo does — one click on a failed case captures the rrweb DOM replay, console, and network, so the Actual Result field is the real reproduction, complete by construction. The developer opens the ticket and watches the failure happen instead of interrogating you about it. A well-written test case tells you what should happen; a captured failure tells the developer exactly what did — and together they close the loop from spec to fix without anyone reconstructing anything from memory.
When a test case fails, capture the Actual Result the way it deserves — a session replay plus console and network in one click, attached and ready for a developer or an AI agent over MCP.
Install the free extensionFrequently asked questions
Frequently asked questions
Sources
- ISO/IEC/IEEE 29119 — the international software testing standard series; test documentation templates live in Part 3, test design techniques in Part 4 — Wikipedia (2026)
- ISO/IEC/IEEE 29119-1:2022 — general concepts of software testing, the vocabulary and framework the rest of the series builds on — ISO (2022)
- ISTQB Glossary — the standard definitions of 'test case', 'precondition', and 'expected result' used across the testing profession — ISTQB (2026)
- ISO/IEC/IEEE 29119-4 — test design techniques: specification-based, structure-based, and experience-based methods for deriving test cases — IEEE (2021)
Get bug-tracking insights, weekly.
Engineering deep-dives, QA playbooks, and honest tool comparisons. No spam — unsubscribe in one click.

