Why write test cases
A test case is a record of one check: the state you start in, what you do and what you expect. With it someone else can run the test, you can repeat it during regression, and later everyone knows what was checked.
This is the third part of the “Testing from scratch” series. The theory behind the techniques in this guide is in the previous part.
Elements of a test case
| element | why |
|---|---|
| ID | so it can be referenced in reports and defect reports |
| title | what is being checked, in one sentence |
| preconditions | the system state before the test: account, permissions, data |
| test data | specific values, not “some data” |
| steps | numbered actions |
| expected result | what should happen; without it you can’t tell whether the test passed |
| traceability | the requirement or user story it covers |
| priority | which tests to run when time is short |
An example
Requirement: during sign-up the user enters their age. People aged 18 to 65 can create an account.
ID: REG-07
Title: Sign-up rejects an age below 18
Requirement: US-112, account sign-up
Priority: high
Preconditions: user not logged in, sign-up form open
Data: email [email protected], password Test!2026, age 17
Steps:
1. Fill in the email and password.
2. Enter 17 in the "Age" field.
3. Click "Create account".
Expected result:
- the account is not created;
- a message about the minimum age of 18 appears under the "Age" field;
- the entered data stays in the form.
Note three things. The title says what is being checked. The data is specific. The expected result has several points, because “doesn’t create an account” is not enough: a form that clears every field after an error is a problem too.
Equivalence partitioning
How many age values are worth checking? You can’t test them all, and picking at random gets you nowhere. Equivalence partitioning splits the data into groups the system should treat the same way:
| partition | example | expected result |
|---|---|---|
| below 18 | 10 | rejected |
| 18 to 65 | 30 | accepted |
| above 65 | 80 | rejected |
| not a number | abc | rejected |
| empty field | (none) | rejected |
You take one value from each partition. Five tests instead of dozens, and each one checks something different.
Boundary values
Most bugs hide at the boundaries. The developer wrote age > 18 instead of age >= 18 and an 18-year-old can’t sign up. That’s why you add values on the boundaries and just beyond them:
| value | why | expected result |
|---|---|---|
| 17 | just below the lower boundary | rejected |
| 18 | lower boundary | accepted |
| 65 | upper boundary | accepted |
| 66 | just above the upper boundary | rejected |
Before you start testing, make sure the boundaries are clear. “From 18 to 65” may mean “up to and including 65” or “before the 65th birthday”. Asking that at the requirements stage saves a defect report that turns out to be a misunderstanding.
Decision table
When the result depends on several conditions at once, a decision table helps. Example: free delivery applies to loyalty card holders or to orders of 200 EUR or more.
| loyalty card | order of 200 EUR or more | free delivery |
|---|---|---|
| yes | yes | yes |
| yes | no | yes |
| no | yes | yes |
| no | no | no |
Four combinations, four test cases. With three conditions there would be eight, so the table quickly shows whether a combination was missed in the requirements.
The most common mistakes
- No expected result. Steps without a result are a user manual, not a test.
- Several checks in one test case. When it fails, you don’t know which part failed.
- Vague data. “Enter a valid email” means something different to everyone.
- Depending on other tests. A test case that only works after the previous one breaks when the order changes.
- Describing the interface instead of the behaviour. “Click the blue button in the top right corner” stops being true after the first redesign.
Test case or checklist
Not every test needs a full description. In a stable product with an experienced team, a checklist is often enough, e.g. “sign-up: age boundaries, email already taken, weak password”. Full test cases pay off where different people run the test, where there are audit requirements or where the test case will later be automated.
Test cases are usually kept in a test management tool (TestRail, Xray, Zephyr) or, in small projects, in a spreadsheet. A spreadsheet is enough to start.
What next
When a test case fails, you need to report a defect, and that is the next part of the series. If you are preparing for an interview, the techniques from this guide come up in the test design section of the interview questions.
The “Testing from scratch” series
- What does a software tester do, and how to get started
- Testing theory in a nutshell
- How to write a test case (this guide)
- How to report defects
- API testing basics
- First steps in test automation