Surprisingly, there’s many programmers who still don’t know what Test-driven development (TDD) is. I think it’s a very important practice that all of us should follow, since it produces software with many less bugs.
TDD is a programming methodology that involves two other practices: test first development, and refactoring. First a set of unit tests is written and verified to fail. Then, code to make them pass must be written, and lastly this code is refactored. The aim of TDD is to achieve clean code that works. The main idea around this methodology is that requirements must be translated into automated tests: thus, when these tests pass, all requirements are guaranteed to have been accomplished. The application to be developed must be flexible enough to allow automated tests to be run. Each test should be small enough to exactly determine which piece of code is failing.
Test-driven development cycle
Once a set of requirements has been defined, a new cycle starts:
- Choose a requirement: select a requirement you understand and about which you can write code both to test it and to implement it.
- Write a test: the first code you must write is a test to determine whether the requirement is met, accessing the code through its interfaces. This step makes the programmer consider the problem from the client perspective.
- Verify the test fails: if the test doesn’t fail it’s because this requirement is already implemented, or because the test is incorrect.
- Write the implementation: Write the simplest code that will make the test pass. Follow the KISS philosophy (Keep It Simple, Stupid!).
- Run the automated test: Verify whether the whole set of tests are passed.
- Refactor: usually used to take out duplicate code. Each change should be made in small increments, and with each increment the tests should be re-run.
- Update the requirements list: once the requirement is met mark it as done. Besides, add more requirements that may have been detected in this cycle or design requirements, like, for example, some functionality that has been decoupled from another one.
Having a repository of tests makes another good practice easier: frequent integration. Integrating our work frequently with the rest of the team allows us to check whether our code is compatible with the rest of the application, and makes it easier to detect bugs in an early phase. It is much easier and cheaper to fix bugs every few hours than facing enormous problems close to a release date.
Generation of tests for each functionality means that programmers can trust the code. This means that we can make big modifications deep in the core of the system that we would otherwise avoid, since we can be certain that, as long as all the tests pass, the code works as intended and the requirements are still met.
Another thing to keep in mind is that programmers must make the unit tests fail. Unit tests must be tested. The idea is to be sure that unit tests really work, and that they’re able to pick up an error whenever it happens.
Programmers that use test-driven development from the beginning of a project find that they seldom have the necessity to use a debugger. In the few occasions that a bug is discovered, it is very easy to fix.
Despite of the high initial requirements of this methodology, TDD may provide a great added value to the software development process, producing higher-quality applications in less time. TDD not only offers a simple requirements validation: it can also lead the design of a program. By writing unit tests, a programmer must imagine how customers will be using the functionality, which may avoid some misinterpretations or bad implementations of a functionality.
In order to follow the test-driven development methodology, tests must be automated. This may turn out to be rather complex in the following situations:
- Graphical User Interfaces, although there are some partial solutions proposed.
- Distributed Objects, even though Mock Objects may help.
- Data bases: writing tests that work with data bases is quite complex, since you need to put some known data before, and you have to verify that the contents of the database is the expected once the test is done. This makes these kinds of tests costly to develop.
Test-driven development is a programming methodology that should be in place in all organizations that develop software and consider themselves serious. Unfortunately, this is not the case. I hope things start to change in the near future!