Skip to main content

Section 9.1 What is TDD

Now that we have a solid grounding on some test-writing basics, it is time to discuss an important development practice, that of test-driven development.
Test-Driven Development (TDD) is the coding practice where we use unit tests not simply to verify that our code works correctly, but also to drive the creation of the code in the first place. In TDD tests and code are written in tandem, each of them never far ahead of the other.
TDD relies on three "laws". These seem hard to follow at first, tempting to skip later, but they are what we can always fall back on when in doubt. I will add a fourth law to the standard list, to emphasize the importance of cleaning up as you go.
3 laws of TDD
  • Don’t write any production code until you have written a test that is failing because of the lack of that code.
  • Don’t write any more of a test than is needed to have it fail.
  • Don’t write any more production code than is needed to pass the failing test.
  • Don’t move to a new failing test until you have performed any appropriate cleanup (refactoring) to both your production code and your tests.
Before seeing this in action, let’s discuss the motivations behind it:
  • Writing a test first forces us to think about how we want to call our code. It forces us to think of the design and the interface first. And our desire to have clean and readable tests forces us to keep our code and design simple.
  • Writing code only in order to pass some failing test ensures that every line of code we put in has some test that backs it up; our tests would be failing without that line of code.
  • Writing the failing test first ensures that this test would truly fail if the code wasn’t there. This avoids something that is surprisingly easy to happen, namely writing a test that we think tests some functionality but it turns out that it really doesn’t.
  • Cleaning up before moving to a new failing test ensures that our code is kept clean at all times, and that we don’t accumulate a series of cleanup tasks that will likely never happen and end up with code that is harder to maintain over time.
  • These small cycles ensure that at almost any given time we have a code base that is fully tested and passes all its tests. To get back to a clean "passing" phase all we have to do is hit "undo" a few times.