A Philosophy of Testing 8: Summary
If you’ve gotten all the way to the end of this series: thank you! This has been a bit of a labor of love, distilling the lessons from many years of building Scala test harnesses whilst building Scala services.
By way of summary, let’s tot up all of those bullet points that have been scattered through the posts, which hopefully make a good reference cheat sheet:
First up, my Golden Rule of Programming, and the other central principles:
Writing code is easy.
Maintaining code is hard.
Therefore, first and foremost, focus on making your code maintainable!
Every developer, as part of their everyday work, should be building test automation.
Test code is code.
Don’t just test by rote. Test mindfully.
Then there are the rules of writing good Scala, and thereby reducing the number of tests you need in the first place:
It is always better to prevent an illegal situation with strong types than to catch it with unit tests.
nullunless you absolutely have to.
Favor immutable data structures whenever possible; use mutability only when truly necessary.
The arguments against most unit tests in Scala services:
Unit tests for plumbing classes are generally a net negative.
Unit tests for plumbing classes provide a false sense of security.
And what you should do instead:
Scenario tests test the application as a whole, transparently, in-process, with heavy instrumentation.
The principles of test coverage:
If at all possible, your Scala code should have automated test coverage, using scoverage or something like that. You should get that to 100% coverage, and keep it there.
It is fine and normal to exempt specific bits of code from test coverage, provided you are disciplined about it.
100% test coverage should be your baseline, not your final goal.
Don’t panic if your test coverage isn’t great — focus on making it a little better every week, and you’ll get there.
Finally, making your tests reliable, and your code testable:
Avoid randomness and non-determinism: strive to make your system as deterministic as possible at test time.
Never, ever use
Instant.nowfor anything other than logging.
Timeouts during unit tests are almost always a bad smell.
Your code must be reliably testable. It should provide whatever hooks are needed in order to test it effectively.
If your code doesn’t provide a good means to test it, the code is incomplete.
Everything external to the code itself should be designed to be test-controllable.
Provide a way for your code to tell the tests about side-effects and non-events.
I’m sure that I will have more to say on this subject over time — I’ll link future posts into here.
In the meantime, comments are welcomed! What have you found to help make for well-tested, reliable Scala services?