Test Driven Development (TDD) has been a buzz-word in the industry for many years now, and with the increasing popularity of frameworks that support isolation of components – and I’m thinking MVC here – TDD is a skill-set that is very much in demand. But is this simply because TDD is being equated with self-testing code, and developers are not standing back and questioning whether self-testing code could be produced in a more productive and engaging way?
Of course I would not consider a work item to be complete unless it has, or has been considered for, a good coverage of workable tests. But I feel a little uneasy with adopting a “always test-first” approach to development. Sometimes it may be beneficial to break away from this approach in order to go with the flow of development. Also, I feel a little uneasy with the prevalent view that you should be able to fully test all units of code in isolation from dependencies, through the use of interfaces and mocking. Testing can be simplified in some circumstances by including dependencies. The number of tests can be reduced, and more effort can be spent on writing good well-factored code rather than on mocking complex objects. I also feel uncomfortable with a drive for 100% test coverage. Does this become an impediment to change?
Don’t get me wrong, TDD is a good approach to development. Just not in all situations. And it’s not the only approach.
The reason I am writing this post is that I’ve just watched a series of Google hangout videos on YouTube – called “Is TDD dead?” – where Martin Fowler, Kent Beck, and David Hannson debate these issues. They are well worth a look –
My take aways from the discussions are as follows –
1) TDD is an approach which produces self-testing code. But self-testing code can also be produced without following the TDD approach. It is not necessary to test-first. Test-after can produce the same test coverage. Self-testing code != TDD.
2) There is a drive in the development community to fully isolate all layers and units of code so that they can be tested independently from other layers and units. Code is therefore designed with unit-testing in mind, and as such may not be optimally designed for comprehension. This drive is a consequence of the uptake in the TDD approach. But TDD equates to red-green-refactor. It does not require the full isolation of units of code, and in many cases the approach can be followed without doing so, as long as the tests are designed to be repeatable. TDD != Unit Testing. But loose coupling of code is not necessarily a bad thing. There are other benefits besides testing. Such as the ability to switch dependencies such as data providers and services.
3) A rigid approach to TDD may lead to over-testing. What is good test coverage? Enough so that you can confidently change the code with the expectation that any errors you introduce will be highlighted by a failed test. But not so much that more effort is spent changing and maintaining tests then on changing code.
4) the TDD approach or discipline is a good one to have, but should not necessarily be followed in all situations. Whether it is used or not, the goal should be to implement a good set of regression tests.