21.1. Test-Driven Development
An excellent practice promoted by the iterative and agile XP method [Beck00], and applicable to the UP (as most XP practices are), is test-driven development (TDD) [Beck00]. It is also known as test-first development . TDD covers more than just unit testing (testing individual components), but this introduction will focus on its application to unit testing individual classes.In OO unit testing TDD-style, test code is written before the class to be tested, and the developer writes unit testing code for nearly all production code.The basic rhythm is to write a little test code, then write a little production code, make it pass the test, then write some more test code, and so forth.Key Point :
The test is written first , imagining the code to be tested is written.Advantages include:
- The unit tests actually get written
Human (or at least programmer) nature is such that avoidance of writing unit tests is very common, if left as an afterthought. - Programmer satisfaction leading to more consistent test writing
This is more important than it sounds for sustainable, enjoyable testing work. If, following the traditional style, a developer first writes the production code, informally debugs it, and then as an afterthought is expected to add unit tests, it doesn't feel satisfying. This is test-last development , also known as Just-this-one-time-I'll-skip-writing-the-test development. It's human psychology. However, if the test is written first, we feel a worthwhile challenge and question in front of us: Can I write code to pass this test? And then, after the code is cut to pass the tests, there is some feeling of accomplishmentmeeting a goal. And a very useful goalan executable, repeatable test. The psychological aspects of development can't be ignoredprogramming is a human endeavor. - Clarification of detailed interface and behavior
This sounds subtle, but it turns out in practice to be a major value of TDD. Consider your state of mind if you write the test for an object first: As you write the test code, you must imagine that the object code exists. For example, if in your test code you write sale.makeLineItem(description, 3) to test the makeLineItem method (which doesn't exist yet), you must think through the details of the public view of the methodits name, return value, parameters, and behavior. That reflection improves or clarifies the detailed design. - Provable, repeatable, automated verification
Obviously, having hundreds or thousands of unit tests that build up over the weeks provides some meaningful verification of correctness. And because they can be run automatically, it's easy. Over time, as the test base builds from 10 tests to 50 tests to 500 tests, the early, more painful investment in writing tests starts to really feel like it's paying off as the size of the application grows. - The confidence to change things
In TDD, there will eventually be hundreds or thousands of unit tests, and a unit test class for each production class. When a developer needs to change existing codewritten by themselves or othersthere is a unit test suite that can be run[1] , providing immediate feedback if the change caused an error.[1] A popular free open source tool to automatically re-build the application and run all unit tests is CruiseControl . Find it on the Web.
The most popular unit testing framework is the xUnit family (for many languages), available at www.junit.org.[2] For Java, the popular version is JUnit . There's also an NUnit for .NET, and so forth. JUnit is integrated into most of the popular Java IDEs, such as Eclipse (www.eclipse.org).
[2] The xUnit family, and JUnit, was started by Kent Beck (creator of XP) and Eric Gamma (one of the Gang-of-Four design pattern authors, and the chief architect of the popular Eclipse IDE).
Example
Suppose we are using JUnit and TDD to create the Sale class. Before programming the Sale class, we write a unit testing method in a SaleTest class that does the following:
- Create a Sale the thing to be tested (also known as the fixture ).
- Add some line items to it with the makeLineItem method (the makeLineItem method is the public method we wish to test).
- Ask for the total, and verify that it is the expected value, using the assertTrue method. JUnit will indicate a failure if any assertTrue statement does not evaluate to true .
- Create the fixture.
- Do something to it (some operation that you want to test).
- Evaluate that the results are as expected.
Only after this testMakeLineItem test method is written do we then write the Sale .makeLineItem method to pass this test. Hence, the term test-driven or test-first development.
public class SaleTest extends TestCase
{
// …
// test the Sale.makeLineItem method
public void testMakeLineItem()
{
// STEP 1: CREATE THE FIXTURE
// -this is the object to test
// -it is an idiom to name it 'fixture'
// -it is often defined as an instance field rather than
// a local variable
Sale fixture = new Sale();
// set up supporting objects for the test
Money total = new Money( 7.5 );
Money price = new Money( 2.5 );
ItemID id = new ItemID( 1 );
ProductDescription desc =
new ProductDescription( id, price, "product 1" );
// STEP 2: EXECUTE THE METHOD TO TEST
// NOTE: We write this code **imagining** there
// is a makeLineItem method. This act of imagination
// as we write the test tends to improve or clarify
// our understanding of the detailed interface to
// to the object. Thus TDD has the side-benefit of
// clarifying the detailed object design.
// test makeLineItem
sale.makeLineItem( desc, 1 );
sale.makeLineItem( desc, 2 );
// STEP 3: EVALUATE THE RESULTS
// there could be many assertTrue statements
// for a complex evaluation
// verify the total is 7.5
assertTrue( sale.getTotal().equals( total ));
}
}
IDE Support for TDD and xUnit
Most IDEs have built-in support for some xUnit tool. For example, Eclipse supports JUnit. JUnit includes a visual cueif all the tests pass when executed, it displays a green bar. This gave rise to the TDD mantra: Keep the bar green to keep the code clean . Figure 21.1 illustrates.
Figure 21.1. Support for TDD and JUnit in a popular IDE, Eclipse.
[View full size image]