Appendix E. Money with ATDD

“Money frees you from doing things you dislike. Since I dislike doing nearly everything, money is handy.”

Groucho Marx

Developers who have been exposed to test-driven development (TDD) often come across the money problem. This problem was introduced in Kent Beck’s book, Test-Driven Development by Example [Beck01]. An example with unit tests accompanies many of the xUnit frameworks.


The Context

An organization owns shares of stocks in different companies, and the shares are valued in different currencies. You want to add up the money in different currencies and convert it to a total in a given currency. An example of this follows, where CHF are Swiss francs.

image


The Original Tests

Kent Beck’s book showed how to develop the code using a test-driven approach. The tests he created using JUnit could be expressed in tables. The first table is for currency conversion and demonstrates how Swiss francs are converted in U.S. dollars.

image

This table is the setup for the remaining tests. Given this table, we need to be sure that we understand how to apply the rate. Do you multiply or divide Swiss francs by 2 to get U.S. dollars? Here’s an example table for that.

image

You need to multiply an amount by the number of shares. The tests for this multiplication operation are expressed in the following table.

image

Finally, you need to add two values that may be in different currencies. There are four possibilities, and this table expresses two of them. Note that the sum is always expressed in USD.

image

The preceding two tests demonstrate our understanding of how addition should work. The sums in the first two cases resulted in USD. Two more tests, which were not in Kent’s book, may help in further understanding of the addition issue.

image

Should the sum of two amounts in Swiss francs result in Swiss francs or U.S. dollars? Should the sum always represent USD, or should it reflect the currency of the first amount? The sums in this table are shown with ?? to reflect that the answers are uncertain and need further definition.

Now we can express the desired calculation in a table. The Accumulated Value is the sum of the Total Values of the current and preceding rows.1

image

These tables do not drive the details of the underlying implementation. That’s what the steps in test-driven design do. These tables were derived from the JUnit tests to demonstrate to the customer how the conversion works. Let’s next approach this from the opposite direction.


The Acceptance Test Approach

Acceptance tests do not indicate how to design a solution. Rather, they are a communication mechanism between the customer, developer, and tester. Let’s start by stating the problem as a user story: “As an accountant, I need to convert amounts that are in different currencies to a total in a common currency.” The role of the accountant is the customer with whom we can collaborate in developing acceptance tests.

Our initial collaboration reveals that currencies are converted into other currencies by applying exchange rates. These exchange rates are time dependent, so we need a context for the conversion. In the context of accounting, the user says that the exchange rates for this conversion will be fixed at an instance of time. He wants all conversions in the reports he is preparing to use the same exchange rates. These rates will be those as of midnight on the day prior to the report being prepared.

Decoupling the issue of conversion from the issue of time-dependent exchange rates allows for easier testing. We can create a separate set of tests for the exchange rates.

We start by making up an exchange rate table. The values in the table are representative of the actual conversion rates. In real life, the conversions may use more digits after the decimal places. They are shown with two decimal digits to keep the example simple.

image

Here are examples of how to convert from one currency to another.

image

The accountant came up with the first example to show what he meant by conversion. The developer created the second example. It shows that the reverse conversion produces a different value than the original conversion. This inconsistency causes a discussion, because the conversion is not symmetric. Is that what is desired? Should there be separate exchange rates for USD to EUR and EUR to USD? Or should one rate be just the inverse of the other rate? Will the non-symmetry cause problems? It’s time for clarification. This is a fundamental issue in the conversion that can affect the rest of the situation. Let’s assume that the accountant decided that the conversion should be symmetric, so we’ll rewrite the setup and the tests.

image

Here are the corresponding examples.

image

The tester suggests a few more examples that come to mind, all dealing with precision. When converting from USD to EUR, the result turns out to have a large number of digits after the decimal point. The ... shows that the digits do not just stop at 2. What should be done with these digits?

image

These examples bring up a discussion of round-off. How should round-off be handled? Should it be tracked by putting the round-off into a separate account (such as the developer’s 401K plan)? Should it be accounted for by some report output? Should the amounts be rounded up or rounded down? Or is the direction of rounding based on certain conditions? There are certain decisions that can be delayed, such as what to do with the round-off. The tests for now can at least ensure that round-off is calculated properly.

image

Next to be developed were a few examples of conversion for multiple currencies. The examples looked like this.

image

The accountant gave the first two examples. The developer wrote the last one. It demonstrates an outstanding issue. Should the amounts be individually converted, or should amounts be totaled before conversion? If the former, the values in the table are correct. If the latter, the table should be corrected to be as follows.

image

The tester comes up with one more example. This one asks the question of how to handle the round-offs between two currencies. Should the converted values be added prior to round-off or afterward? This test needs a second example to show that the underlying implementation does things correctly.

image

Now the developer understands the overall picture of the problem. She can pick one of the acceptance tests and use that as the starting point for a test-driven design. Unit tests, some of which may be derived from these examples, can check that the classes and methods are giving the desired behavior, such as the round-off. Passing these acceptance tests demonstrates to the accountant that the conversion module as a whole performs as desired.


Summary

• You can use acceptance tests to derive unit tests.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.60.63