Guidelines
A test is code that outlives the code it tests. It gets read far more often than
it gets written, almost always by someone who did not write it, usually at the
worst possible moment — the build is red, the release is today, and the failure
message says expected true, got false.
These guidelines are about the properties that decide whether a test earns its maintenance.
| Page | The property |
|---|---|
| Writing a test | Intent — a reader can tell what is being specified and why |
| Determinism | The same input gives the same result, every time |
| Coverage | Measuring what was verified rather than what was executed |
| Reporting | A result someone can act on without re-running it |
The four questions
Section titled “The four questions”Before writing a test, and again in review:
What could be wrong? Name the defect specifically. If you cannot, the test will assert whatever the code currently does, which is a change detector rather than a specification.
Is this the cheapest level that can catch it honestly? See typologies. If something already checks it, stop.
Will this fail for exactly one reason? A test that can fail for six reasons gives one bit of information about six things.
Will the failure message name the cause? This is the difference between a five-minute fix and an afternoon.
The one rule everything else serves
Section titled “The one rule everything else serves”A test asserts behaviour, never structure.
Structure is what a class is called, which methods it has, how many times a collaborator was invoked, what the internal state looks like. Behaviour is what an observer outside the boundary can tell.
A suite bound to structure breaks on every refactoring while catching nothing, and it teaches the team the most expensive lesson available: that refactoring is costly. It is not. Over-specified tests are.
The practical test for this: could I rewrite the implementation entirely, keeping the same observable behaviour, and would this test still pass? If not, the test is asserting how rather than what.