#56: Test-driven development: It's not about testing
Test-driven development (TDD for short) means developing software by writing tests first. I hope you all write unit and integration tests. But do you write them before the actual production code? This approach to software development is just that. You must write a failing test first. And you are not allowed to write even a single line of production code without a failing test. Think about it. If all your tests are green, it is forbidden to write production code. Everything must start from a red test.
OK, let’s formalize that. TDD cycle consists of three stages: red, green and refactor. You always start from writing one, failing, red, test. Then you write as little production code as possible to make that test green. This is crucial. Do not add any extra code not justified by a failing test. Only the bare minimum. Once the test is green, you are free to refactor. Refactoring means cleanup, renaming, extracting or deduplicating code. But not adding any new features. Now the cycle repeats: write a failing test, make it green, refactor.
There’s one interesting side effect of TDD. It’s virtually impossible to have less than 100% code coverage. You need code for a special edge case? Start from the test. Or maybe there’s some extra error condition or exception to catch? Make a test for that first.
Notice that TDD doesn’t say “testing first”. It rather says development driven by tests. TDD is not really about testing. It’s an attitude to designing and developing software that must be testable, by definition. What do I mean by that? For example, all dependencies are extracted and well defined. There are no hidden side effects and we promote small, focused functions and methods. This is pretty much enforced when doing TDD. Otherwise, it’s impossible to start from the test. The test drives the design. It’s questionable whether TDD alone is enough to produce a good design. But it certainly doesn’t hurt.
I don’t always practice TDD, but I know developers who follow it religiously. If you are like me, at least give it a try or exercise TDD through code katas. A code kata is deliberate practice. There are three exercises I can recommend. All involve pair programming.
The first exercise is where one developer writes the test and the other writes production code. The first one then refactors and they switch the roles. This is known as ping-pong.
The second exercise involves a short-timer, like 2 to 5 minutes.
During that cycle, one developer must write the test and the other makes it green.
Then you do
git commit and restart the timer.
If the test is not green (or code doesn’t even compile) during that short period, you start from scratch.
This exercise additionally teaches keyboard shortcuts and staying away from your mouse.
You must be fast!
After a few unsuccessful attempts, you know what needs to be done, but you need to be very effective.
The third exercise I can recommend is also similar to ping-pong, but only one person writes the tests. Incidentally, that’s also the only person who knows the requirements! The developer writing production code may only rely on tests, no prior knowledge. To make this even spicier, you may forbid any verbal communication, just code.
OK, is TDD really worth it? Most of the time it does lead to a better design. At least more testable. But also it may produce a ton of trivial test cases. Sometimes that’s undesirable. All code is a liability, after all. However, in general, following TDD consciously will almost always improve your codebase.
That’s it, thanks for listening, bye!