In this chapter, we define test-driven development (TDD) and then describe a process for applying it when developing software.
Kent Beck, in his book Test-Driven Development: By Example (Addison-Wesley Professional, 2003), defines test-driven development using the following rules:
Never write a single line of code unless you have a failing automated test.
The first rule is straightforward: don t write code without having a failing automated test because the tests embody the requirements that the code must satisfy , as stated in the Introduction. If there is no requirement (that is, test), there is no need to implement anything. This rule prevents us from implementing functionality that is not tested and not needed in the solution.
The second rule states that no code should be duplicated in the program. Code duplication is the epitome of bad software design; it leads to inconsistency problems and a reduced level of confidence in the program over time as people forget where the duplications are. If there is code duplication, it is the programmer s job to remove it when it is seen. (In Extreme Programming [XP], this rule is called Once and Only Once! )
Kent s definition does not make a distinction between different types of tests  . All he says is that the tests have to be automated, which we agree with entirely. However, it is useful to categorize the test types around the constituents who produce them ”for example, customers who specify the functionality, programmers who implement the functionality, and testers who support development during the development of the code and critique the final result after the code is complete. The two constituents that this book focuses on are programmers and customers.
|More Info|| |
For information about testers, see Brian Marick s website ( http://www.testing.com ).
If we are talking about the programmers writing the code, it is useful to think of these tests as focused on technology, so you can refer to them as technology facing or programmer tests . Some people refer to this type of test as a unit test; we are specifically not calling it that because unit testing s purpose is much different from what we are doing in TDD. Because the audience for these tests is programmers, it is critically important that the tests be written in a language that the programmers understand. (It s even better if they are in the same language as the production code.) If the tests are in the same language, the programmer doesn t have to change paradigms to write tests. The programmer tests in this book are written in C# and are run in a tool named NUnit ( http://www.nunit.org ).
Tests that customers use to specify the functionality they need and how it should work can be referred to as business facing or customer tests . These types of tests are often referred to as acceptance tests or functional tests . As in the case of programmer tests, the tests need to be written in a language that the customer understands. The goal is to empower the customer to be able to write tests. The customer tests in this book are written using a tool named Framework for Integrated Test (FIT) ( http://fit.c2.com ).
Simplicity is more complicated than you think. But it s well worth it (Ron Jeffries et al., Extreme Programming Installed , Addison-Wesley, 2001).
In a previous section, TDD was defined as a couple of rules. However, that definition leaves out a practice that is implied by the rules but not stated explicitly. Because the tests define the requirements, your job when writing the code should be to satisy the requirements no less and no more . Everyone understands no less (the program would not work otherwise ), but not everyone understands no more .
What is meant by no more ? Think back to a time when someone asked you to add a feature to an existing system, and you said No problem; I thought this was going to happen and put additional code in for this specific purpose. You were viewed as a hero because you anticipated the requirement and had already implemented the solution.
Now remember a time when you added complexity in the form of additional functions, abstract classes, and so on that nobody ever asked for. This additional code has to be maintained along with the rest of the useful software. In fact, the maintenance burden of this software is worse because it is not supported by real usage. So you should always strive to do no more and no less , as follows :
The code is appropriate for the intended audience.
The code passes all the tests.
The code communicates everything it needs to.
The code has the smallest number of classes.
The code has the smallest number of methods .
The highest priority is that the code be appropriate for the audience. For example, if you are writing a parser for youself to read, you would likely use a generator because you are the person reading the code. If you were writing a parser for someone new to parsing, you might use a parsing technique called recursive descent to better illustrate how parsing works. Either approach is suitable; you just need to keep in mind who the audience for your solution is. After you have satisfied the appropriateness requirement, the next -highest priority is that the code must pass all the tests. When all the tests pass, the code has to communicate the intent as clearly as possible. (Code can be duplicated if the purpose is to better communicate the design intent.) The last two criteria state that the implementation should have the smallest number of classes and methods after the first three criteria are satisfied.
You might think that achieving simplicity is an easy process. Think again ”it s often very difficult. However, the simpler the code is, the more resilient it is and the easier it is to modify.
Refactoring is defined as improving the code while not changing its functionality. The definitive book on refactoring is Martin Fowler s, Refactoring: Improving the Design of Existing Code (Addison-Wesley, 1999). This is a book you should own and read cover to cover. (Even though the examples are in Java, the concepts presented transcend the language.)
Refactoring is a critical part of TDD because you need to refine the code s design as you add additional tests. For example, if you see duplicated code in the solution, you need to remove it. If you need to introduce complexity to remove the duplication, it is all right because there is an actual need, not an anticipated need. Following this practice diligently yields an implementation that is complex where it needs to be; no more and no less. It is through refactoring and simple design that you can refine the system s design to meet the requirements specified by the tests.
|More Info|| |
See Chapter 3, Refactoring ”By Example, for a step-by- step demonstration of refactoring.
 The categorization that we present here comes from Brian Marick (http://www.testing.com). Brian has written about the role of testers on Agile projects and TDD in general. We encourage you to visit his website