Every tester has stared at a one-line requirement — "the user can reset their password" — and wondered where to start. Writing good test cases from requirements is not a talent; it is a repeatable method. Mine has four steps: hunt the ambiguity, expand acceptance criteria into positive, negative, and edge cases, apply boundary analysis and equivalence classes, and keep traceability light enough that you actually maintain it.
Step 1: hunt the ambiguity first
Before writing anything, interrogate the requirement. "The user can reset their password" hides a dozen open questions. How is the reset delivered — email, SMS, both? Does the link expire, and after how long? Can an unverified account request one? What happens on the third failed attempt? Every question you ask in refinement is a bug that never gets written. I keep a running list of ambiguity triggers: words like "quickly," "should," "supported," any passive sentence that hides who acts, and any description that covers only the happy path.
Step 2: turn acceptance criteria into test cases
Each acceptance criterion generates three families of cases:
- Positive: the documented behavior works exactly as stated.
- Negative: the system refuses what it should refuse — wrong input, wrong state, wrong user.
- Edge: the strange-but-legal territory — empty values, maximum lengths, repeated actions, interrupted flows.
The order matters. Positive cases confirm the requirement; negative and edge cases test the requirement itself, and they are where most of the real defects live.
Step 3: boundaries and equivalence, in plain language
Equivalence classes sound academic, but the idea is simple: if two inputs will take the same path through the system, you only need one of them. For a password field that allows 8–64 characters, testing 25, 30, and 42 characters tells you nothing that testing 30 didn't. Boundary analysis is the other half: bugs cluster at the edges, so test exactly at the limits — 7, 8, 64, and 65 characters — not somewhere comfortable in the middle. Together the two techniques cover more behavior with fewer cases, which also keeps the suite cheap to maintain later.
Step 4: traceability without heavyweight tools
You do not need a requirements-management platform to know which cases cover which requirement. A naming convention does most of the work: prefix each case with the story ID — PWD-12-pos-01, PWD-12-neg-03. In JIRA, linking the test task to the story gives you the reverse direction for free. When a requirement changes, one search tells you exactly which cases need review. That is the whole point of traceability: not an audit artifact, but a fast answer to "what do I need to update?"
A worked example
Take this requirement: "Users can apply one discount code per order; codes are 6–10 alphanumeric characters."
- Ambiguities: is the code case-sensitive? What message appears for an expired code? Can a code be removed and replaced before checkout? Does "per order" survive an abandoned cart?
- Positive: a valid 6-character code applies; a valid 10-character code applies; the discount is reflected in the total and at payment.
- Negative: a second code is rejected with a clear message; 5- and 11-character codes are rejected; a code with symbols is rejected; an expired code is rejected.
- Edge: apply, remove, and re-apply the same code; apply a code, then change the cart below its minimum spend; paste a code with a trailing space; submit while offline.
Ten minutes of method produced more than a dozen cases, and at least three of them — the trailing space, the cart change, the replace flow — would never appear if I had only read the requirement and "tested that it works."
Takeaway: before writing any test cases, write down every question the requirement does not answer. The questions are the real test plan; the cases are just their formal version.
The method costs discipline, not time. And it compounds: the negative and edge cases you invent for one feature become a personal checklist you reuse on every feature after it.