“Test-driven development” refers to a style of programming in which three activities are tightly interwoven: coding, testing (in the form of writing unit tests), and design (in the form of refactoring).
It can be succinctly described by the following set of rules:
- write a “single” unit test describing an aspect of the program
- run the test, which should fail because the program lacks that feature
- write “just enough” code, the simplest possible, to make the test pass
- “refactor” the code until it conforms to the simplicity criteria
- repeat, “accumulating” unit tests over time
Expected Benefits
- many teams report significant reductions in defect rates, at the cost of a moderate increase in an initial development effort
- the same teams tend to report that these overheads are more than offset by a reduction in effort in projects’ final phases
- although empirical research has so far failed to confirm this, veteran practitioners report that TDD leads to improved design qualities in the code, and more generally a higher degree of “internal” or technical quality, for instance improving the metrics of cohesion and coupling
Common Pitfalls
Typical individual mistakes include:
- forgetting to run tests frequently
- writing too many tests at once
- writing tests that are too large or coarse-grained
- writing overly trivial tests, for instance omitting assertions
- writing tests for trivial code, for instance, accessors
Typical team pitfalls include:
- partial adoption – only a few developers on the team use TDD
- poor maintenance of the test suite – most commonly leading to a test suite with a prohibitively long running time
- the abandoned test suite (i.e. seldom or never run) – sometimes as a result of poor maintenance, sometimes as a result of team turnover
Origins
While the idea of having test elaboration precede programming is not original to the Agile community, TDD constitutes a breakthrough insofar as it combines that idea with that of “developer testing”, providing developer testing with renewed respectability.
- 1976: publication of “Software Reliability” by Glenford Myers, which states as an “axiom” that “a developer should never test their own code” (Dark Ages of Developer Testing)
- 1990: testing discipline dominated by “black box” techniques, in particular in the form of “capture and replay” testing tools
- 1991: independent creation of a testing framework at Taligent with striking similarities to SUnit (source)
- 1994: Kent Beck writes the SUnit testing framework for Smalltalk (source)
- 1998: article on Extreme Programming mentions that “we usually write the test first” (source)
- 1998 to 2002: “Test First” is elaborated into “Test Driven”, in particular on the C2.com Wiki
- 2000: Mock Objects are among the novel techniques developed during that period (source)
- 2003: publication of “Test Driven Development: By Example” by Kent Beck
By 2006 TDD is a relatively mature discipline which has started encouraging further innovations derived from it, such as ATDD or BDD).
Signs of Use
- “code coverage” is a common approach to evidencing the use of TDD; while high coverage does not guarantee appropriate use of TDD, coverage below 80% is likely to indicate deficiencies in a team’s mastery of TDD
- version control logs should show that test code is checked in each time product code is checked in, in roughly comparable amounts
Skill Levels
Beginner
- able to write a unit test prior to writing the corresponding code
- able to write code sufficient to make a failing test pass
Intermediate
- practices “test driven bug fixing”: when a defect is found, write a test exposing the defect before correction
- able to decompose a compound program feature into a sequence of several unit tests to be written
- knows and can name a number of tactics to guide the writing of tests (for instance “when testing a recursive algorithm, first write a test for the recursion terminating case”)
- able to factor out reusable elements from existing unit tests, yielding situation-specific testing tools
Advanced
- able to formulate a “roadmap” of planned unit tests for macroscopic features (and revise it as necessary)
- able to “test drive” a variety of design paradigms: object-oriented, functional, event-drive
- able to “test drive” a variety of technical domains: computation, user interfaces, persistent data access, etc.
Further Reading
Test Driven Development: By Example, by Kent Beck