©Vladimir Levin
TDD: Test-Driven Development
What is TDD
TDD, or Test-Driven Development is is one of the core practices of Extreme Programming (XP),
but it is a useful practice even on its own. TDD is a programming practice in which
all "production" code is preceded by an automated test (also often called a unit test).
The goals of TDD are as follows:
- Drive design and programming activity
- Avoid analysis paralysis (spending excessive time on analysis and design without
producing any tangible functionality)
- Promote simple design to avoid bloat (a design with a high degree of
complexity that does not offer a significant benefit over a simpler design)
- Increase quality of application by decreasing number of defects
- Improve robustness: Make changes to an application more easily because the tests
will catch errors that such changes might introduce
- Make bug fixes permanent by introducing a unit test for each bug that is
identified
- Enhance productivity: Allow code to be added/modified in small incremements rather
than writing a bunch of code over a period of hours or days, then having to test
all of it in one big chunk
Basic Loop of TDD
- Write a test and make sure it fails
- Write code to make the test pass
- Examine the design and refactor (see note) if necessary, then make sure tests still pass.
Note: Refactoring should be done when the design starts to look bad (long methods,
unclear responsibilities) or when making changes or writing additional code
becomes cumbersome. Refactoring is the process of modifying the structure of
code without changing its underlying purpose. A simple example is breaking up
one long function into multiple shorter functions, each with a clear
responsibility
TDD is not
- An algorithm generator: If an algorithm of some complexity is required, it is
unlikely that TDD will converge on the appropriate algorithm all by itself. However,
it may be a good framework in which to think about different approaches.
- A substitute for thinking. As you develop in TDD, you must still spend time in
the refactoring step considering whether the design is evolving in the right
direction, based on your knowledge of what remains to be done. Its goal in
this respect is simply to reduce the tendency to over-design or to fall into analysis
paralysis.
- A replacement for quality assurance or acceptance/regression testing. One must
still make sure the code satisfies the customer's requirements and that
it all works together end-to-end.
Drawbacks of TDD
- Major redesign will involve having to modify a large number of
tests as well. As far as I know, this is a "cost of doing business"
with TDD. It's hard to imagine any major redesign effort being simple,
with or without TDD.
- Using TDD to write user interfaces is not easy (there are frameworks
for that sort of thing though)
- Interfacing with external resources (databases, files, sockets) can be tricky
but it is usually doable through the use of appropriate frameworks (e.g. DBUnit
for database tests) or with the use of mock objects and stubs (a mock object,
or stub, is a stand-in for a real object that exists only for the purpose of
a test).