If you've ever spoken to me for more than five minutes in a professional context, you've probably heard me drone on at length about the benefits of Test-Driven Development (TDD). But, uncharacteristically, I've never before written down my thoughts on the practice.
So let's do that.
TDD IS A DESIGN PRACTICE, NOT A TESTING PRACTICE
First and foremost we need to address the elephant in the room: the 'Test' in 'Test-Driven Development'.
I know 'Test' is the first word, which seems like it implies 'testing'. But it doesn't! Trust me! Look at a similar construction: "Ox-Driven Cart" (ODC). Now, with an ODC are we focusing on Oxen? The answer is 'no'. The operative word is Cart. We're using oxen to drive the cart.
Similarly, in TDD we use tests to drive design.
That's not to say that TDD can't be part of a larger testing strategy. But remember, you're using tests to drive design. As the design of the code shifts and evolves it may obviate some of your earlier design-driving tests, and you need to be free to delete them. You won't be free if your testing strategy relies too heavily on your design-driving tests, and those old tests will drag you down like the Sadness dragged down Artax.
And nobody wants that.
So what is TDD then?
TDD is the practice of incrementally building scaffolding that allows you to be optimally free to design your application habitably.
... which is a mouthful, so let's break it down.
- practice - TDD is a practice. It's a way of coding that is different than sitting in front of an IDE, plugging away, then running the application and seeing where you screwed up (or, at least, where I screwed up). It's a fundamentally different way to approach programming. It is also very structured - there are rules and you must exercise discipline to hold to them.
- incrementally - TDD is incremental. You are, in a step-by-step fashion, codifying the constraints that your application must meet in order to be complete, all in the context of 'tests'. Every new test you add is a new constraint that fixes the behavior of your application, nudging it closer to completion.
- scaffolding - As you incrementally add more tests you are essentially constructing behavioral guardrails that your application must stay within. Further, these guardrails are external to the application itself, much like scaffolding isn't a part of the building it's supporting. So at some point the scaffolding can be ignored or removed when the application itself is freestanding (assuming a robust testing strategy).
- optimally free - There are two extremes of test coverage. In the first, you aren't at all free to refactor your code because the tests are so tightly coupled to the implementation that changing anything in your production code ends up breaking at least one test. At the other end of the spectrum there are no tests and making a change in one area of production code may break functionality somewhere else, so you have little confidence in your ability to modify any code. TDD is the sweet spot: the tests cover the behavior of the system, so you can modify your implementation (as long as you're not changing behavior), confident that you aren't breaking tests.
- habitably - In this sense I mean "able to be inhabited comfortably". Your code shouldn't be like "that shadowy place" that Simba is forbidden to go to. It should be something you genuinely enjoy working in, something that you take special care to keep clean. You don't want to keep piling functionality in a single class - you want to refactor to have small classes with distinct, comprehensible responsibilities. You're building a park, not a landfill.
So, taking it all together, TDD is a new way to code where you write - step by step - constraints that exercise units of your application in such a way that you can refactor easily and keep your code cleaner.
Remember, the name of the game is design.
Enough for one day
So that's what TDD is. In Part 2 I'll talk about some of the mechanical aspects, how to actually do TDD. As is tradition it will be replete with nonsensical, inapplicable and specious examples.