Testing as Design Feedback

Hard-to-test code is usually telling you something about its dependencies. Where to put the boundary, what to double, and when mocks start mirroring implementation.

Testing as Design Feedback

Code that is miserable to test is usually hiding a dependency or mixing two jobs. That is real information — and it is not a licence to bend the production design around a test runner.

Q · This code is painful to test. Is that a testing problem, a design problem, or neither?
What a Unit Is

A "unit" is not a class and not a method. It is a boundary you have chosen to hold stable — which makes choosing it a design decision, not a testing convention.

Q · Which behaviours deserve their own test, and what should the test be allowed to know?
Where a Test Must Be Real

Some abstractions are load-bearing precisely because the thing underneath them is complicated. Replacing those with a double tests your belief about the dependency rather than the dependency.

Q · Which parts of this design are only meaningfully tested against the real thing?
Mocking

Mock at boundaries you have chosen to keep stable. Mock every internal collaboration and the suite becomes a cast of the implementation — and then it argues against the refactoring it was supposed to enable.

Q · Which collaborations deserve a mock, and which ones should the test simply let happen?
Test Doubles, Precisely

Stub, fake, mock, spy and dummy are not synonyms. They differ in what they know and therefore in how they fail — and picking the wrong one is how a suite ends up brittle or blind.

Q · When I substitute a collaborator, what exactly am I substituting — and what does that substitution stop the test from being able to detect?
Contract Tests

A contract test is the thing that keeps a double honest. It is also a design decision: writing one is a declaration that this seam is a contract and not an implementation detail.

Q · Two sides of a boundary each have green tests. What proves they agree?
Property-Based Testing

When a behaviour can be stated as something true of every input, you can test the statement instead of a handful of examples — and being unable to state one is itself a finding about the design.

Q · Can this behaviour be expressed as something that must hold for all inputs, and what does it mean if it cannot?