By which characteristics do we judge whether a unit test is clean? I know it when I see it, but what is it I’m seeing that tells me a test is clean or not?
There are lots of things we strive to achieve in writing and refactoring a test: Use intent-revealing names. Be as specific as necessary but no more. Establish and convey a clear focus. Independence. Repeatability. Robustness. Avoid conditional logic. And so on…
There are lots of patterns we apply: Creation methods. Custom assertions. Matchers. Verification methods. And so on…
It’s all been written about extensively, and having those simple names and definitions makes it easy to talk about them, to remember what to look for, to apply them.
I’ve come to realise that there is a simple name we can use to describe one of the main qualities exhibited by a clean test: A high signal-to-noise ratio.
If you have any familiarity with science, maths or engineering, and you probably do, then you probably instinctively grasp the concept.
What’s the signal? If you consider that one of the main purposes of a test is to express the specification of correct behaviour (the other main purpose being to verify and protect that behaviour), then the signal is the expression of that specification.
The situations that are significant and the behaviour required in those situations.
What’s the noise? It’s the code that is only there to make the test compile and run, to keep the compiler happy. It’s the wiring and engineering that lets things happen but is not an important part of telling the story of the test.
Think of Uncle Bob’s newspaper style. The test method itself is the lead paragraph (the method name is the headline). It’s the high level overview that gives the reader a quick understanding of what scenario we’re establishing and what we require should happen in that case. Arrange, Act, Assert. The Given-When-Then. It should be fluent and allow you to quickly grasp the author’s intent, the desired behaviour, and get a feel for whether or not it is correct.
Someone said that if it’s not important that a line appears in the test, then it’s important that it not appear. Following this rule leads to a high signal-to-noise ratio.
How do you achieve a high signal-to-noise ratio?
Factor out the engineering. Don’t make people read a load of lines of low-level Java. Use creation methods. Use constants and local variables with intent-revealing names. Use custom matchers. Use verification methods.
For each line, each word, ask yourself whether this is an essential part of telling the story of this test, or is it just plumbing? If it’s plumbing, hide it. If it’s essential, make sure it’s clear and fluent and easy to read.
A high signal-to-noise ratio indicates that you have a clean test.