Test-driven development is usually presented as a developer process. On the other hand, acceptance test-driven development (ATDD) is a communication process between the customer and the developer. In ATDD, the tests provide the terminology in customer-understandable terms. The customer's terminology suggests abstract data types that make code more readable.
Recently I was reviewing the agile development process at a client where the development group was implementing acceptance test-driven development. The client's agile teams include both a business analyst and a system tester. Working together, they are developing acceptance tests using the Framework for Integrated Testing (FIT). FIT tests are represented by tables embedded within HTML pages. Each table specifies test cases consisting of values of inputs and expected outputs.
When creating test cases, text-based business rules are converted to tables that give examples of those rules. For instance, suppose the business rule for giving a discount is:
If Customer Rating is Good and the Order Total is less than or equal $10.00,
Then do not give a discount,
Otherwise give a 1% discount.
If Customer Rating is Excellent,
Then give a discount of 1% for any order.
If the Order Total is greater than $10.00,
Then give a discount of 5%.
These requirements might be interpreted in multiple ways. For example, if the rating is excellent and the order is greater than $10.00, is the discount 1 percent or 5 percent? Using a table to illustrate the rule can decrease misinterpretation.
Table 1 gives examples of this business rule.
The FIT framework transforms this table into calls to a fixture. Fixtures convert the values in an HTML table to assignments to variables and the execution of methods. User-written fixtures inherit from predefined classes provided by the FIT framework. The variables and methods are defined by the column headers. A header with the "()" suffix denotes a method. For each row in a table that references a column fixture, FIT sets the variables, executes the method, and compares the returned result to the expected value in the method column.
The name of the fixture DiscountFixture is the first line of the table. The fixture has public data members with the names orderTotal and customerRating and a public method called discountPercentage(), which returns a percentage (implemented as a double). Listing 1 shows how this fixture would be implemented in Java.
Assume that CustomerRating is an enumeration with a method that converts a string such as "Good" to a constant as CustomerRating.GOOD;.
Creating this table in advance of coding helps clarify the requirements of the system. It also helps the developer create a readable set of tests. In my book Prefactoring , I give some guidelines for readability. One of them is to "Use the Client's Language." Within the FIT table, the client has specified the requirements in his own language. The names of the table columns should be used in the code that implements the business rules. For example, the method should look something like:
double computeDiscount(double orderTotal ,
CustomerRating customerRating) ;
In the event that the client specifies the column headers as "ord_tot", "cust_rat", and "disc", then you should use those as the names of the parameters and the method name in the FIT table. That avoids having to do a mental translation of terms during communication between the client and the developers.
Now, you can take another step and apply the "When You're Abstract, Be Abstract All the Way" guideline. The values representing orderTotal and discountPercentage() are often represented by doubles. But that's an implementation issue. The client refers to them as dollars and percentages. Separating implementation from use guarantees more readable code. So, create classes that represent these abstract data types. The computeDiscount() method is shown in listing 2.
This Java may look a little odd to the client (particularly with all those "new" keywords). However, if the terms match those by which the client