← Back to blog
Testing FundamentalsBy

Test Cases in Software Testing: Format, Examples, and a Template That Works

Most test case templates are a form nobody fills in properly. Here is the shorter version that survives contact with a real team.

What a test case is

A test case is a single, specific check with a known starting state, a defined set of actions, and one expected result. The test for whether you have written a good one is simple: could two people run it independently and agree on whether it passed?

That bar sounds low. Most test cases in most organisations do not clear it, usually because the expected result is stated vaguely enough that both a pass and a fail could be argued.

Case, scenario, script

  • Test scenario — the broad thing being verified. “A customer can apply a coupon.” Too wide to pass or fail as stated.
  • Test case — one concrete path through the scenario, with a specific expected result. “Applying an expired coupon shows an expiry message and applies no discount.”
  • Test script — the executable implementation, in Playwright, Cypress, or whatever your team uses.

One scenario typically yields four to eight cases. And historically, the expensive part was never writing the cases — it was the translation from case to script, which required an engineer and had to be redone every time the UI moved.

A template worth using

Most templates in circulation have fifteen fields. In practice teams fill in five and leave the rest at their defaults, which makes the extra ten actively harmful — they teach everyone that the template is bureaucracy.

FieldStatusWhat it is for
Test case IDRequiredA stable identifier you can reference in a bug report. CHK-004, not “the coupon one”.
TitleRequiredOne line describing the check, written as the condition. “Expired coupon is rejected”.
PreconditionsRequiredThe state the system must be in before step 1. Signed in, cart contains one item, flag on.
StepsRequiredNumbered, one action each. If a step contains “and”, it is two steps.
Expected resultRequiredWhat should be observable when the last step completes. One outcome, stated specifically.
Actual resultAt run timeWhat actually happened. Left blank until executed.
PriorityOptionalOnly if you genuinely run subsets by priority. Otherwise it is decoration.
Test dataOptionalAdd when the case depends on specific values. Inline it in the steps if it is short.

The field that does the work is “expected result”. If it says “the coupon is handled correctly”, the case is worthless. If it says “an error reading ‘That coupon has expired’ appears and the discount line is absent”, it is a real check.

Four worked examples

Abstract advice about test cases is easy to agree with and hard to apply. Here are four real ones from a demo storefront, in the format above.

CHK-001 — Expired coupon is rejected

Preconditions: cart contains one Ridgeline 2P Tent ($289.00), no coupon applied Steps: 1. Open the cart 2. Enter the coupon code SPRING10 3. Click Apply Expected: a message reading "That coupon has expired" is shown, no Discount line appears, and the total is unchanged

A cart showing the message That coupon has expired after applying the SPRING10 code, with no discount line in the summary
CHK-001 passing on the correct build. The assertion is on two things: the message appearing and the discount line not appearing.

CHK-002 — Out-of-stock product cannot be added

Preconditions: none Steps: 1. Open the store home page 2. Locate the Squeeze Water Filter, which is marked out of stock Expected: its Add button is present but disabled, and clicking it adds nothing

CHK-003 — Empty checkout form is rejected

Preconditions: cart contains at least one item Steps: 1. Open the checkout page 2. Click Place order without entering anything Expected: every required field shows "This field is required", no order is created, and the page does not navigate

A checkout form with required-field errors shown under every empty input
CHK-003 passing. The negative assertion — no order is created — matters as much as the visible errors.

CHK-004 — Valid coupon applies the correct discount

Preconditions: cart contains one 300lm Headlamp ($44.00) Steps: 1. Open the cart 2. Enter the coupon code NORTH20 3. Click Apply Expected: the Discount line shows -$8.80 and the order total is $48.23

Note how much of the value sits in the exact numbers. “A discount is applied” passes even when the discount is wrong, which is precisely the silent regression this whole discipline exists to catch.

Running them for real

We ran CHK-001 and CHK-002 together against a build where both rules had been broken — expired coupons accepted, out-of-stock items addable.

Real Diffie run · demo.diffieai.comTest failed
What we asked for

Case A: applying the expired coupon SPRING10 must show "That coupon has expired" and must not apply a discount. Case B: the Squeeze Water Filter is out of stock; its Add button must be disabled.

What came back

Both cases failed as intended. The replay shows SPRING10 applied with a −$28.90 discount line where there should have been an expiry message. Two discrete, enumerable failures — which is exactly the shape a test case suite is built to report.

The test case text above was the entire input. No selectors, no waits, no page objects were written by a person.

How many cases, and which ones

Counting test cases as a productivity measure reliably produces large numbers of worthless ones. A better heuristic: for each feature, cover four categories and stop.

  • The happy path. One case. The thing the feature is for.
  • Each failure the user can cause. Wrong code, empty field, expired card, insufficient permission. One case each.
  • Each boundary. If a rule changes at $100, test $99.99 and $100.00. Boundaries are where off-by-one bugs live and nowhere else.
  • Each entry state. Signed in versus anonymous, empty cart versus full, first-time versus returning.

Writing cases that survive a redesign

  • Name things the way a user would. “The Checkout button”, not “the primary CTA in the summary card”.
  • Assert on values, not positions. “The discount shows −$8.80” beats “the third row shows −$8.80”.
  • State the negative. Half of all acceptance bugs are things that should not have happened. “No discount line appears” is a real assertion.
  • One expected result per case. A case with four expected results is four cases wearing a coat, and when it fails you cannot tell which part broke.
  • Never depend on data you did not set up. “The first product in the list” is a bug waiting for someone to reorder the catalogue.

The step that used to cost the most

Look again at CHK-001. It is complete, unambiguous, and anyone on the team could execute it. The traditional next step is for an engineer to spend twenty minutes converting it into a script — finding selectors, adding waits, handling the toast that disappears after four seconds — and then to maintain that script forever.

That translation is where test case suites historically went to die. The cases stayed in a spreadsheet, the scripts covered a fraction of them, and the two drifted apart until nobody trusted either.

The run above removes the translation. The text of the case, pasted in as written, is the test. The case and the script are the same artefact, so they cannot drift.

A well-written test case has always been executable by a human. The only thing that changed is that it is now executable by a machine without a rewrite.

Frequently asked questions

What is a test case in software testing?

A test case is a single, specific check with a known starting state, a defined set of actions, and one expected result. It should be unambiguous enough that two different people running it independently would agree on whether it passed.

What is the difference between a test case and a test scenario?

A test scenario is the broad thing being verified — "a customer can apply a coupon". A test case is one concrete path through it — "applying an expired coupon shows an expiry message and applies no discount". One scenario usually produces several test cases.

What is the difference between a test case and a test script?

A test case describes what to check in human terms. A test script is the executable implementation of it. Historically a person wrote the case and an engineer converted it into a script, and most of the cost of test automation sat in that translation step.

What fields should a test case template have?

An ID, a title, preconditions, steps, expected result, and actual result. Everything else — priority, module, author, severity, environment — is optional and should only be added if someone genuinely filters on it. Most templates collapse under fields nobody reads.

How many test cases should a feature have?

Enough to cover the happy path, each failure the user can cause, each boundary value, and each state the feature can be entered from. That is usually between three and eight for a small feature. Counting test cases as a productivity metric reliably produces large numbers of worthless ones.

How do you write test cases that do not break constantly?

Write them against observable outcomes rather than implementation. "The discount line shows $8.80" survives a redesign. "The third row of the summary table contains 8.80" does not. The more your case describes markup, the shorter its life.

Keep reading

Skip the translation step

Write the test case once, in plain English, and Diffie executes it directly in a real browser. No engineer needed to turn it into a script.