Goals of Test Driven Development

Why should you adopt Test Driven Development?  The answer is actually more complex than many people think.

Of course we all want to have fully tested code to ensure that the code operates as expected.  We also want to ensure that any new features do not break existing functionality (regressions), but what other reasons are there?

1. Documenting code behavior by providing examples of usage: Developers can look at a unit test in order to better understand what the code is supposed to do.  A good test might also test exotic inputs (null values and invalid parameters) to ensure that they are handled correctly.  Such tests also demonstrate to readers of tests exactly what the code expects for input.  This is especially helpful when working in a team practicing “Collective Code Ownership” and in the highly likely case that a different developer will one day work on the code after the original author has moved on.

2. Ensure code testability: Writing unit tests for existing code can be a very frustrating experience, especially if code is too tightly coupled with other classes or a test harness cannot easily be constructed.  The cost can easily outweight the benefit in such cases and often it is simply too expensive to write tests for more than small pieces of untested code.  If this happens then you might have to just accept that your code isn’t ever going to have decent test coverage.  If tests are written before they are implemented then the implementation will implicitly be “testable”.  When new features are added to the class, it will be trivial to add a new unit test for the new logic.

3. Testable code is also well designed code: The reason why testable code is testable is because it has a clean separation of concerns and is usually well encapsulated.  This is means that your testable code is also well designed code.  Yes… writing tests will actually improve your code design.

4. Trying out new code first helps make its API better: How many times have you written a class that you’ve been proud of only to find out that when you actually start using it you find yourself wishing that the interface had been ever so slightly different.  Often some subtle (and unforseen) reason surfaces once you start actually trying to use your code that means that you have to change the methods on that class.  By writing a test first you are creating a client for your code.  This gives you the opportunity to look at how the design of the class from a unique perspective.  Perhaps the method calls could be made more readable with some small change, or perhaps some of the parameters are superfluous.  By trying out your class before it even exists, you can write your implementation code with the knowledge that it will be easy to use by client code.

5. Refactoring becomes a breeze: Actually, you shouldn’t really refactor code unless it has a unit test ensuring its correct operation.  The unit test should be run frequently between refactoring steps to ensure that your changes do not in fact modify the class behavior.  If programmers can refactor with a safety net then they are much more likely to actually do it, meaning better code for everyone.

Notice that 3 of the benefits only apply if the test is written before the implementation (i.e. using Test Driven Development).  By writing tests afterwards you are doing at least the same amount of work but missing out on some key benefits.

Writing unit tests does not always make sense however:

Contrary to popular belief, writing unit tests does have a cost associated with it which may be greater than the benefit.  More often than not, unit tests are seen as a holy grail which should always be applied.  Simply writing tests does not automatically mean that your project will be fault free and it certainly doesn’t automatically mean that your team will be more effective.  Instead, it should to be looked at like an monetary investment.  You will be incurring a real cost with the hope of recouping some benefit.  It may be that you lose out if you are not careful, but equally you can reap huge benefits if you do it right.  You should consider whether or not the benefit outweighs the cost on a case by case basis.  The factors for consideration should be:

1. How many developers will work on this project during its life? Better code APIs and documentation through tests means that the need for unit tests increases with the number of developers working on the project.

2. What is the skill level of your developers? Writing a good unit test is not easy.  Having poorly written tests which test the implementation too rigidly, or that are non repeatable, or slow, or test only trivial logic may incur a great deal of maintenance overhead.  It might be worth investing in some training before blindly jumping on the TDD bandwagon.  Developers should know what makes a good unit test and what makes a bad one.

2. Where is the project in its lifetime? If a project is nearing the end of its life and you are sure that it won’t be developed further then writing tests might not make sense.  You are making an investment with the hope that you can reap a benefit in the future.  If there is no future for the project, then the effort could be better invested in other ways.  A project just starting out however can benefit greatly over its lifetime with little initial investment.

On a final note:

Generally, bad test practices breed more bad test practices and good test practices breed more good test practices.  A team that is not performing well with regard to unit testing will find it only gets harder and increasingly more expensive to start.  A well performing team will find that writing tests and refactoring becomes a breeze and will continues to do so.  If you want to make a really good job of it make sure that you get it right, right from the project kick off.

Tags: , , , , ,

Comments are closed.