Skip to content

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

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.

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.