The testing cycle goes from red to green to red to green
One of Kent Beck’s first articles about unit testing is called Test Infected: Programmers Love Writing Tests. It was written, I think, in 1998. What’s interesting about the Test Infected article is how Kent describes the process: “code a little, test a little, code a little, test a little”.
Which is backwards. Or not-backwards, depending on your perspective, but in any case, it’s not the TDD process that Kent would eventually describe in the XP book, among other places.
I’m going somewhere with this. The question is: what do you gain and what do you lose by writing the tests before the code?
When you write the test first you are specifying the desired result in some detail before you write any code. I think this has two main benefits:
- By making the desired result clear before you start, you know when you are done. (Or put another way, because you start with a failing test, if the test passes, you know you’ve actually changed some behavior.)
- In writing the test, you have the chance to create the interface to your code by actually calling it from the test.
I usually find the second benefit more valuable in practice. Writing the test first lets me explore the problem a bit, deciding what parts of the app need to be public and which can be hidden. If writing the test starts to feel burdensome, that often means that the code is not properly structured, and as a result I need to do too much setup for my unit test.
That said, writing tests first is hard.
Sometimes I don’t know enough about the code to figure out what I want the structure to be and I find it easier to do that discovery in code rather than by testing. In particular, if I’m testing against some kind of view output, I find it nearly impossible to determine what I want to test for until I code up at least part of the view. This is often true of other kinds of integration code, I’m just not sure what I’m testing until I see it.
I suspect (but, of course, can’t prove) that it’s not uncommon for a novice to give up on TDD precisely because it can be so hard to write a test for code that doesn’t exist yet.
Writing the code first swaps out the exploration feature of writing tests in exchange for the possibility that writing a useful test is easier.
If you write the code first, you will get most of the benefit of TDD provided you still have some structure:
- You still work in small steps. You write one method then write the test. Writing the tests is less valuable if you are writing pages and pages of code before you try to test.
- You still use the test as a check on how good the design of the code is. Instead of exploration, this test that you write after the code is not just validating the result of the code, it’s validating the design. You still need to go back and fix the code if the test is hard to write.
- You make sure the test can fail, so you still know the test is doing something rather than nothing.
In other words, while “write a small test, then write some code” is still pretty great, “write a small amount of code, then write a small test” has most of the benefit for sometimes a much smaller cost.
The problem with coding first is that there’s a natural tendency for the amount of time between tests to get longer and longer. This tends to make the tests harder to write because the more code you write before the test, the more likely you are to insert dependencies or side effects in the code, meaning the test needs more setup or is more brittle.
Overall, though, it’s never a bad time to write a test, as long as the test actually covers code that might fail or change, and if you are willing to use both result of the test and the process of writing the test as information about the quality of your code.