Friday 18 September 2009

TDD is a complete failure.


What I mean by that is when you design a method using TDD, you start with a failing test, and you then implement code to make the test pass.


The test defines your expectations of the code that you will be designing. When writing the test, think in terms of what a client or consumer would expect of the method.

Let me demonstrate. Imagine some sort of financial accounting system. We have a list of credits, money going into the account and a list of debits, money leaving the account.

For the sake of this demonstration, let's have the two sets of values in two arrays:

int[] credits = {23, 42, 117, 11, 48};
int[] debits = {93, 22, 38, 29, 14};

Our first test will look like this. (This is written in C#)
[TestMethod]
public void Calculate_CreditsGreaterThanDebits()
{
int[] credits = {23, 42, 117, 11, 48}; // Total: 241
int[] debits = {93, 22, 38, 29, 14}; // Total: 196
Account account = new Account();
int balance = account.Calculate(credits, debits);

// We expect the result to be 45, that’s (241 - 196)
// In TDD we can perform various checks using something called an assert.
// In this case, I just want to check that the balance is equal to 45.

Assert.AreEqual(45, balance);

// If the balance is anything other than 45, the test will fail.
}
First of all our code won't compile, so add the new method Calculate with the minimum of code to enable compilation.
public class Account
{
public int Calculate(int[] credits, int[]javascript:void(0) debits)
{
return 0;
}
}

The project will now compile. Run the test and we will get the following failure.

Assert.AreEqual failed. Expected:<45>. Actual:<0>.

That's good news. In TDD we love a failing test. We can now do the enjoyable part of writing real code to make the test pass.

public class Account
{
public int Calculate(int[] credits, int[] debits)
{
int totalCredit = 0;
int totalDebit = 0;

foreach (int credit in credits)
totalCredit += credit;

foreach (int debit in debits)
totalDebit += debit;

return totalCredit - totalDebit;
}
}
Run the test again and it now passes

Notice the name of the test, Calculate_CreditsGreaterThanDebits.

The first part, Calculate, indicates the target method that I am designing. The second part describes the scenario that I am designing for.

The name of the class that contains this test is AccountTests. This naming convention means that this test class will be used to design the methods in the Accounts class.

Having got this one scenario working, you then implement a few more scenarios such as:

Calculate_CreditsLessThanDebits()
Calculate_CreditsEqualToDebits()

No comments:

Post a Comment