BlogDocsProduct Log in Start free
Guides

Manual Testing Documentation: Writing for the Person Who Isn't You

A guide to documenting manual tests so they survive being handed over — how much detail is right, what belongs in a test case, and the reuse machinery that stops maintenance from eating the suite.

A test case broken into its parts, with the shared and parameterised pieces drawn out as reusable blocks.

Manual testing documentation is a set of instructions precise enough that someone other than the author can follow them and reach the same conclusion. That’s the whole bar, and almost every decision about how much to write follows from taking the second half of it seriously.

The person you’re writing for isn’t you and isn’t today. It’s a new joiner in eight months, a contractor during a crunch, or you after a year on another product. Documentation that only works for someone who already knows the system isn’t documentation — it’s a memory aid, and it fails silently at exactly the moment it’s needed.

How much detail is actually right

The most common argument in QA is between fully scripted cases and lightweight charters. Both camps are right about the other’s failure mode.

Detailed scripts
Lightweight charters
Anyone can execute it
Yes (leads)
No
Cost to maintain
High
Low (leads)
Finds unexpected problems
Rarely
Often (leads)
Usable as audit evidence
Yes (leads)
Partial
Survives a UI redesign
No
Yes (leads)
Detailed scripts leads 2 · Lightweight charters leads 3
The choice isn't which is better — it's which risk you're carrying.

A fully scripted case — click here, type this, expect that — can be run by anyone and proves exactly what was checked. It also breaks the moment a button moves, and a suite of them becomes a maintenance job that quietly consumes the time you meant to spend testing.

A charter — “explore checkout with expired cards, focus on error messaging” — survives redesigns and finds things scripts never would. It also can’t be handed to someone unfamiliar, and it’s weak evidence when someone asks what was tested.

The practical answer is to vary detail by risk rather than picking a house style: scripted for the paths that must not break and for anything you’ll need to prove; charters for exploration and for areas under active redesign. Writing everything at the same level of detail is the actual mistake.

What belongs in a test case

A title that says the outcome

Test checkout is a label. Guest checkout with an expired card shows a retryable error is a title — it says the scenario and the expectation, so someone scanning fifty results knows what broke without opening anything.

Preconditions, stated as state not steps

Preconditions describe the world before the test starts: a user of this type exists, this feature flag is on, the cart contains two items. The mistake is writing them as instructions (“go to admin, create a user…”), which turns setup into untracked test steps that nobody counts and everybody re-derives.

Steps that are one action each

A step should be one thing a person does. When a step contains three actions and one of them fails, the result is ambiguous — and “failed at step 4” stops being a useful location. Atomic steps also make the eventual automation obvious.

Expected results at the step that produces them

The most common structural error is one expected result at the bottom of a twelve-step case. If the observable outcome happens at step 4, the expectation belongs at step 4. Otherwise a tester has to hold the whole case in their head to know whether what they just saw was right.

Data that’s real enough to be wrong

test@test.com and Test User are fine until the test is about validation rules. Where the data matters, state it exactly — and where it doesn’t, say so, so a tester doesn’t waste ten minutes wondering whether the specific value mattered.

The maintenance problem

Documentation debt is the thing that kills manual suites, and it accumulates in a specific way: the same setup sequence gets copied into two hundred cases, and then the login flow changes.

Three pieces of machinery deal with this, and they solve genuinely different problems.

Shared steps solve duplicated procedure. Write the login sequence once, use it in every case that needs it, change it in one place. The thing to check when choosing a tool is what happens to runs already in flight when the shared step changes — if there’s no versioning, improving a shared step silently rewrites the history of every run that used it. That problem, and where the version pin has to live to solve it, is worth understanding before you rely on reuse.

Parameters solve duplicated values. A base URL, an admin account, an environment name referenced in eighty cases shouldn’t be typed eighty times.

Datasets solve duplicated cases. When five cases are identical except for the input — five card types, five user roles — they’re one case and a table. That collapses five things to maintain into one, and it means adding a sixth variation is a row rather than a case.

Documentation rots, so plan the maintenance

Test documentation goes stale in a way that isn’t visible from the document itself. The case still reads fine; it just no longer describes the product. It’s the same failure a spreadsheet has, only without the tab that says Notes2 — and it’s why generated cases carry a label saying what evidence they were written from, rather than arriving as plain prose nobody can date.

Two habits help more than any tooling:

  • Update the case in the same change as the feature. If updating tests is a separate ticket, it becomes a backlog item, and backlog items about test maintenance do not get done.
  • Delete aggressively. A case for a removed feature isn’t neutral — it’s noise in every search, an extra thing to review, and eventually somebody’s wasted afternoon.

And accept that not everything needs to be a documented case. A one-off verification can be a comment on the ticket. Reserving the repository for tests that will genuinely be re-run keeps it something people trust.

How this works in Hawzu

The principle underneath all of it is simple: document what has to be repeatable, make variation explicit rather than duplicating cases, and avoid copying anything that will drift. Most of what follows is machinery for those three ideas.

A test case carries a title and description, priority, severity, test type, automation status, its folder, any linked requirements, labels, and whatever custom fields your project defines. Test type is multi-select and grouped — category, purpose, level and style — so a case can be Functional and Regression and End-to-End rather than forcing one label to carry three meanings.

Preconditions are a separate block, kept out of the steps rather than smuggled in as step one. Worth knowing precisely: a precondition is a single rich-text block with attachments, images and variables, but it has no expected output field of its own — it describes state, and the assertions live in the steps.

Steps come in three modes. In the default Multiple mode, each step has a description and its own expected output, and both are independent rich-text editors with their own attachments — so a screenshot sits next to the observation it illustrates rather than in a pile at the bottom. Single collapses that to one description and one expected output. Gherkin writes the case as Given/When/Then with live syntax highlighting, parsed into clauses a tester marks individually.

Automation status is a first-class field with four values — Manual, To Be Automated, Automation In Progress, Automated — which is more useful than a boolean, because “we intend to automate this” is a real state that otherwise lives in somebody’s head.

All three reuse mechanisms are present, and the distinction between authoring and execution matters. Shared steps are inserted as a live link, so improving one reaches every case using it — that’s the point. The versioning happens one level down: when a case is pulled into a run, the run pins the version it used, so improving a shared step can’t rewrite runs already in flight. The run shows that its source has moved on and the tester applies the change deliberately, after seeing a step-level diff. Parameters cover shared values, inserted with $. Datasets turn one case into several: variables referenced with {{name}}, and each dataset row becomes its own independently assignable, independently statused item in a run — so five card types are one case and five results rather than five cases. Renaming or removing a referenced variable shows an impact summary listing every step that uses it.

Every change is recorded with its previous and current values in a Change History tab, so “when did this expectation change, and to what” is answerable rather than a matter of memory. Note that history is read-only — there is no revert. You can see what changed; you can’t roll it back.

Three things aren’t there. No review or approval workflow, no test case templates, and no duplicate detection while you’re authoring.

The first two are decisions. Approval states existed once and were taken back out — nobody’s job was to move cases through them, so nothing ever moved. Templates lost to cloning, which is what people reach for anyway. The third is a genuine gap rather than a considered choice: the AI generator avoids repeating itself within a run, but writing a case by hand won’t tell you something similar already exists.

Key takeaways

  • Write for someone who wasn’t there. That single constraint settles most arguments about detail.
  • Vary detail by risk — scripted where it must not break or must be provable, charters for exploration. Uniform detail is the actual mistake.
  • Put the expected result at the step that produces it, and write it before the steps.
  • Use shared steps for duplicated procedure, parameters for duplicated values, datasets for duplicated cases. They aren’t interchangeable.
  • Check what happens to a shared step’s history. Linking live is right for authoring; without version pinning at execution time, improving one rewrites every run that used it.
  • Update tests in the same change as the feature, and delete aggressively.

Common questions

How detailed should a manual test case be?
Detailed enough that someone unfamiliar with the feature reaches the same conclusion you would. That means high detail for critical paths and anything you'll need as evidence, and much less for exploratory work or areas being redesigned. Writing everything at the same level is the common error.
What should a manual test case contain?
A title stating the scenario and the expectation, preconditions written as state rather than instructions, atomic steps, an expected result attached to the step that produces it, and data specified where it matters. Plus the metadata that makes it findable later — priority, area, and any linked requirement.
Should test steps be written for beginners or experts?
For someone competent but unfamiliar. Writing for a complete beginner buries the point in navigation detail; writing for an expert produces a case only the author can run — which fails precisely when they're unavailable.
How do you stop test documentation from going stale?
Update the case in the same change as the feature, so it never becomes a separate backlog item. Delete cases for removed features rather than leaving them. And keep one-off verifications out of the repository entirely, so what's in there is genuinely re-run.
What's the difference between shared steps, parameters and datasets?
Shared steps remove duplicated procedure — one sequence used in many cases. Parameters remove duplicated values like a URL or an account. Datasets remove duplicated cases — where several cases differ only by input, they collapse into one case and a table of rows.
Do test cases need a review or approval process?
Usually not, and it often backfires. Review states only work when someone's actual job is to move cases through them; without that owner, everything sits in draft forever and the state becomes noise. Peer review at the pull-request level, or a periodic audit, tends to work better than a formal gate.

Continue learning