Writing Your First Test Plan
The capstone: for one real feature, write down what you will test, what you will not, in what environment, with what data, and how you will know when you are finished.
The capstone: for one real feature, write down what you will test, what you will not, in what environment, with what data, and how you will know when you are finished.
"Did you test the refunds?"
There are two ways that question can go. One is "I think so — let me check", followed by twenty minutes of looking, and whatever the answer turns out to be, nobody trusts it much. The other is "no, deliberately: refunds are out of scope for this release, it says so in the plan, and here is why."
The second answer takes one page written before the testing started. That page is what fifteen lessons of technique turn into: for this feature, in this release, here is what we will check, here is what we will not, and here is what we will be able to say at the end.
By the end of this lesson you will have written one, and you will know why the section people skip is the most valuable one in it.
A plan makes three things explicit before the testing starts, and each one prevents a specific argument later.
So the question has an answer, not a defence.
Naming what you will not test is what makes the rest credible.
So the time goes where the danger is.
Otherwise the hours go to whatever somebody happened to start with.
So the release call is about evidence.
Rather than about how confident everyone feels on the day.
None of that needs a long document. The traditional test plan — thirty pages, a standard template, sections on tooling and staffing — is right in a regulated environment and mostly wastes time everywhere else. One page somebody reads beats thirty nobody does.
Nine short sections. Most will be two or three lines.
FEATURE What is being tested, in one sentence
Link to the requirement or ticket
RISKS What could go wrong, worst first
Why each one matters
IN SCOPE What will be tested, and at which level
OUT OF SCOPE What will NOT be tested, and why
APPROACH How: scripted cases, exploratory charters,
automation, non-functional checks
ENVIRONMENT Where it will be tested, on which build
DATA What data is needed and where it comes from
DONE WHEN The conditions for saying testing is complete
RESIDUAL What we still will not know at the end
RISKThe two that carry the most weight are out of scope and residual risk, and they are the two that get left out. A plan that only says what will be tested implies everything else is covered.
For a real feature: a CSV import for bulk-creating records.
One page. Every section is short, and it says something a reader could act on.
The risk list is the plan. Four prompts produce it, drawn from earlier lessons:
What would be worst? Data loss, exposure between customers, money handled wrongly, an action that cannot be undone. Start here, not with what is likely.
What is new or has changed? Bugs cluster in recent change. A feature that is entirely new is entirely risk.
What broke before? In this area, in this kind of feature, in this codebase. The difficulty that caused the last defect has not gone away.
What does the requirement not say? Every gap you found reviewing it is a risk, because it will be resolved by whoever writes the code, silently.
Then order by consequence rather than by likelihood. A rare event that corrupts data outranks a common one that misaligns a button.
"Done when" is the section that turns a plan into something a release decision can rest on, and its quality depends entirely on being observable.
Bad — cannot be checked, so it will be decided by feel:
Good — every line is a fact somebody can verify:
The bad version has a specific failure mode: at 5pm on release day, "working well" means whatever the person under the most pressure needs it to mean. There is no way to say "not yet" without it being an opinion against a deadline, and no way to say "yes" that means anything.
The good version can be read out in a meeting. If two of five lines are unmet, that is a fact, and the decision to ship anyway becomes a deliberate, recorded choice by whoever is entitled to make it — which is the right outcome. Your job is to make the trade-off visible, not to prevent it.
The last section is the one that most distinguishes a professional plan, and it is the hardest to write because it is an admission.
Residual risk is what you still will not know when testing is complete. Not what you failed to do — what was deliberately not covered, and what could therefore go wrong in production.
Write it because the alternative is worse. Without it, "testing is complete" is heard as "the feature is safe", and it never means that. With it, the person making the release decision knows exactly what they are accepting, which is the only condition under which they can accept it responsibly.
It also protects the work. When the untested concurrency case causes an incident in three months, the question "why did nobody think of this?" has a written answer: somebody did, said so, and recommended a fix.
You have the foundations: what testing is for, how software breaks, the levels, how to design cases, boundary analysis, static review, exploratory sessions, bug reports, triage, regression, data and environments, automation, a first automated test, the non-functional dimensions, and now a plan that ties them together.
The natural next step is Testing in Practice, which takes all of this into a real codebase: structuring a suite, the unit and integration boundary, faking external services, fixtures and factories, API and browser automation, flaky tests, CI, coverage, and the metrics worth reporting.
Before that, write a plan for something you are working on now. One page, nine sections, risks ordered by consequence, and both of the sections people leave out. It is the single most useful artefact in this course, and the first one you will be asked for.
FEATURE
Bulk import of contacts from a CSV file, for account admins.
Ticket: PROJ-812.
RISKS (worst first)
1. Partial import leaves inconsistent data and the user cannot
tell what was saved. Recovery is manual and expensive.
2. Re-running the same file duplicates records. Duplicates in a
contact list corrupt downstream mail sends.
3. A large file blocks the request and times out with no
feedback; the user retries, compounding risk 2.
4. An admin of one account imports into another. Data exposure
between customers.
5. Malformed rows are accepted and stored as bad data — dates,
phone numbers, encodings.
IN SCOPE
Unit: row parsing and validation — every field's
boundaries and invalid forms
Integration: import writes correctly, and rolls back on failure
System: the API contract, permissions, error responses
End-to-end: one journey — upload, see the result, see the
records
Exploratory: two charters (below)
Non-func: a 10,000-row file, timing and query count
OUT OF SCOPE
Export (unchanged in this release — PROJ-780 covers it)
Files over 10 MB (rejected by the upload limit; that limit is
tested separately)
Excel formats (not supported; a rejection message is in scope)
Localised number and date formats (deferred to PROJ-830)
Load beyond one concurrent import per account (no requirement
yet — see residual risk)
APPROACH
Scripted cases for each validation rule and each permission
case, driven at API level where possible.
Two exploratory charters:
- explore the import with files malformed in different ways,
to discover how partial failures are handled
- explore the import as each role and across accounts, to
discover whether anyone can write where they should not
Automate: validation unit tests, the API permission cases, one
end-to-end journey. The 10,000-row check stays manual for now.
ENVIRONMENT
Staging, build 2.15.x or later. Version endpoint checked before
each session.
DATA
Created per test through the API: two accounts, an admin and a
standard user in each.
Fixture files committed alongside the tests: valid 2-row, valid
10,000-row, bad date in row 2, missing header, UTF-8 BOM, empty
file, non-CSV renamed to .csv.
DONE WHEN
All scripted cases run, with results recorded.
Both exploratory charters run and reported.
Every Critical and High defect fixed and verified.
Automated tests merged and green in CI.
The 10,000-row import completes within 30 seconds.
No unresolved Critical or High defect remains open.
RESIDUAL RISK
Concurrent imports into one account are untested. If two admins
import at once, behaviour is unknown — plausibly duplicates.
Recommend either a requirement or an application-level lock.
Localised formats are untested and will fail for non-UK
customers; PROJ-830 must land before the EU rollout.
Files between the 10 MB limit and whatever the browser tolerates
are untested.DONE WHEN
Testing is complete and the feature is working well.DONE WHEN
All 34 scripted cases run; results recorded in the run sheet.
Both exploratory charters run and reported.
Every Critical and High defect fixed and verified.
Automated tests merged and green in CI.
10,000-row import completes within 30 seconds on staging.# A test plan is one page that makes three things explicit
# what is in and out of scope
# where the effort goes, and why
# what "done" means
# Nine sections
FEATURE one sentence + a link to the requirement
RISKS what could go wrong, worst CONSEQUENCE first
IN SCOPE what will be tested, and at which level
OUT OF SCOPE what will NOT be, and why <- often omitted
APPROACH scripted / exploratory / automated / non-functional
ENVIRONMENT where, and on which build
DATA what is needed, and where it comes from
DONE WHEN observable conditions, each able to fail
RESIDUAL RISK what you still will not know <- often omitted
# Write the risks first; everything else follows from them
what would be worst? data loss, exposure, money, no undo
what is new or changed? bugs cluster in recent change
what broke before? the same difficulty is still there
what does the requirement every gap will be resolved silently
not say? by whoever writes the code
# order by consequence, not by likelihood
# DONE WHEN — make every line checkable
bad "testing is complete and the feature works well"
good "all 34 cases run and recorded; both charters reported;
every Critical and High fixed and verified; automated
tests green in CI; 10,000 rows within 30 seconds"
# an unverifiable bar is decided by whoever is under most
# pressure at 5pm on release day
# RESIDUAL RISK — write it, always
# what was deliberately not covered, and what could go wrong
# "testing is complete" is heard as "it is safe". It never means that.
# it makes the trade-off visible so someone can accept it knowingly