April 14, 2014
Test Driven Development (TDD)
Test Driven Development (TDD) is one of the core practices of Extreme Programming and a best practice that is becoming common place across the financial industry and beyond.
TDD is about writing tests first, before you write the code. In essence the tests drive the code, hence the name.
How does it work?
TDD is similar to BDD and in fact BDD is really a subset of TDD. When people talk about TDD they are also referring to lower level unit tests as well as the acceptance tests that operate at an interface level of BDD.
Unit tests are written alongside the code using a testing framework such as JUnit for Java or NUnit for .Net. Tests are written in the same language that the code they test is written in.
When the developer is tasked with writing some functionality, the developer first writes the test. They run the test and the test should fail. They then write the code and re-run the test. The test should now pass.
Why is this a good thing?
There are many reasons why TDD is a good thing. Here are some of them.
- TDD focuses the developer on writing only the code required to make the test pass.
- Each test can execute different code paths and ensure each one operates as expected.
- When refactoring, tests ensure that developers haven’t accidentally broken something they didn’t mean to and don’t know about it.
- Tests change the way developers write code. To be able to test small parts of the code, it is necessary to ensure code is not dependent on child objects. It is necessary to use dependency injection to create dependency and this produces less coupled code which is easier to maintain.
- Tests explain the functionality of the code. Tests are a way of documenting what the code is supposed to do as well as ensuring that they do it. See the BDD section for more details on Live Documentation.
- Tests provide a second opinion on your code. If you code something twice, once in your code and once in your test, it is extremely unlikely you will make the same mistake twice. Any mistakes are made visible immediately, thus reducing bugs and improving quality.
What makes a good test?
There are many opinions about what makes a good test. Some universally accepted good practices are:
- Tests should be able to be run in any order.
- A test should test a single thing only.
- Tests should be well laid out and easy to read.
- Tests should be able to be run quickly so that they don’t become a hindrance to development and therefore skipped.
- Tests should not store state across tests. Each one should be able to be executed entirely on its own.
Problems with unit testing
The main problem with unit testing is that unit tests can become tightly coupled to the code and thus become ‘brittle’. This means that changing the code requires changes to tests and therefore increases the length of time to change anything considerably.
This increase of development time can lead tests to be skipped and so code gets written that is not covered by tests.
For this reason, I recommend a combination of unit tests to test specifically difficult to test low level features and acceptance and integration tests to cover everything else.
Is it ok to not test features?
Michael Feathers raised the definition of Legacy code to be ‘Legacy code is code without tests’.
In other words, if you don’t have tests, how do you know what it does and if it does it correctly?
Aim for as high code coverage as possible.
Code coverage is the percentage of code that is executed when the supporting suite of tests is run. You can find out the code coverage by using tools such as NCover and DotCover for .NET and see http://en.wikipedia.org/wiki/Java_Code_Coverage_Tools for code coverage tools for Java.
The code coverage tools can be run at the same time as the unit tests are run locally on the developer’s environment and on the build server when code is checked in.
TDD is a very important part of development and anyone looking for a developer role should understand and practice writing tests in the appropriate xUnit tool against their code.
I recommend reading the following: