TDD seems to be more and more a buzzword these days than years gone by, particularly in the .NET world. Every agent and potential employer seems to be interested in it. Yet I see more and more people that are chucking around the TDD word on their CVs / LinkedIn profiles and don’t even use it on a day to day basis. Some say that they “sometimes” practice it (when?), others that they know what it is (which is presumably good enough), or some that they simply write unit tests (which is apparently the same thing as TDD).
Given this, I want to post a little reminder on what TDD is, and what it is not.
Test Driven Development or Development Driven Tests?
Let’s be clear – there’s a big difference between what I call TDD and its inverse, DDT. The former has a well known three-step structure know as Red-Green-Refactor: –
Write a failing test
Write enough code to make the test pass
Repeat ad infinitum. Sounds easy doesn’t it? As it turns out, it’s surprisingly easy to accidentally drift from this well-established process into the later DDT process.
How to break from TDD?
Firstly, the “failing test” bit is there for a good reason, although it’s often not clear to people what this is. It’s there to help to ensure that your test actually tests what it’s supposed to, i.e. if you write a failing test that doesn’t go red initially, what’s the point of fixing it? Worse still, you could write a test that doesn’t actually test what you think it should do, think that your code passes, and then discover the bug later (these are some of the worst kinds of bugs you can get when not performing TDD in a disciplined manner).
Another kind of lapse is to simply treat unit testing as TDD. Sadly these two distinct practices have been grouped together as one because TDD really brought unit testing to the fore (at least, within the .NET community). However, I’ve interviewed many a developer before who says that they practice TDD but when asked about it, they simply mean “they write unit tests”. This is all well and good – but I’d rather people simply said that rather than suggest that they “do” TDD.
Why not DDT?
Personally, I’m not a big fan of simply writing unit tests after the fact. Firstly, you don’t know when you’ve got complete test coverage – don’t assume that code coverage of 100% means that you have complete coverage. All it means is that you’re run though every line of code under test. A more accurate indicate of more complete code coverage is “if I comment out a single line of code, will I break at least one unit test”. Secondly, you don’t get any of the free warning signs that TDD gives you about violating SRP. When you write tests after the fact, you can end up with god classes that are difficult to fully test. What you then get is a set of tests that often don’t fully cover all paths of your code, but just what you consider to be the “main” paths. Sadly, those are often the ones that you don’t need to test as much as the exceptional cases. It’s actually very difficult to retrospectively write unit tests for production code to a high degree of coverage.
The process of TDD uses unit tests as a mechanism for driving production code. The act of writing unit tests alone is not enough to say “I know TDD” or “I practice TDD”.