A black cat in the middle of taking a step down in a steep staircase.

My favourite way to create something big that works, is to start with something small that works. Test Driven Development has the benefit of resulting in automated regression tests, but that is not my main reason to like it. According to Theory of Constraints starting with verification reduces waste. To me the tests are a sounding board in the thinking process of creating software. So I write one test at the time, before I write the code, and here is why:

Specific feedback

In the red-green-refactor cycle, when a test is red, that was expected to turn green, we need it to provide feedback. When the code is not doing what we think it should, one red test has a lot more information than ten. With only one red test at the time, we can be sure that it is the latest change we did that is not working. When writing trivial code this will never happen. But not all code is trivial, and some code get less trivial just before lunch or during a bad day.

Design guidance

Code that is designed to be tested is also designed to be reused. Tests are code using your units. When figuring out the interaction between code and test, the API of the unit will probably change. With great refactoring tools changing multiple tests are quickly done. But still, having to repair something, that has not brought value yet, is wasteful.

Just the right amount of tests

It can be hard to know how many tests that is enough, before you start implementing them. Tests are code that have to be maintained, and take time to run. So having just enough tests is really valuable. If the first step is to invent all tests that might be useful, our inventive power might make us a bit too productive. We might create tests that end up being waste. One effect of red-green-refactor is that no tests can be written for code that is already implementing the functionality tested, since you need to start with a test that is red. That prevents us from writing duplicate tests.

Just like a backlog contains stories to implement later, a test file can contain a list of test descriptions, names, or corner cases. That can be our outer loop. For the inner loop I want red-green-refactor, for one test at a time, one step at the time.

ps. If the code got too clever too early and you think that you might not have full test coverage — there is a solution. To make sure that everything the code does is covered with tests, mutation testing can be used. Mutation testing is when you change the code to test if it is covered by a test. A “mutant” is introduced by changing a constant, swapping a conditional or something else that should modify the functionality of the code. When a mutant is introduced and no tests go red, you can write a new one that does fail. That test is then red, and when the code is restored, it turns green. That way there will be only the tests needed, and hopefully all the tests needed.

This text was last modified 2023-09-14

Related texts

A single frosted leaf with clear contours and veins.

TDD in the context of writing code to be read

Wunder Baum hanging from a car window (Known as Little Trees in most English-speaking countries)

Wunderbaum testing