Learning TDD

A friend described recently what happened when his some of his team started learning TDD.

They were doing the Roman Numerals kata, where you write a class to convert arabic numerals into their roman equivalents. So for example; an input of ‘7’ gives an output of ‘VII’.
The code to support the first test was easy.

public String arabicToRoman(int input) {
    return "1";
}

So was the second.

public String arabicToRoman(int input) {
    if (input == 1) {
        return "I";
    } else {
        return "II";
    }
}

However by the time they got to the third there was a problem. The code turned into a switch statement. Everyone knew that as the numbers got larger, a better algorithm would be needed, but didn’t know when make the change.

What was missing?

One of the key steps in TDD is to refactor to remove duplication after you get the test working. That’s key, but isn’t always fully appreciated.

  • Duplication refers not just to code blocks but of logic as well. That seems to be the case here
  • The open-closed principle may also help when deciding when to refactor. In this case the switch statement must be modified every time a number is added.

Refactored logic might look like this. An aggressive refactorer might even have done this at the second step:

public String arabicToRoman(int arabic) {
    String roman = "";
    for (;arabic > 0; arabic--) {
        roman = roman.concat("I");
    }
    return roman;
}

Of course the algorithm will continue to evolve as we add support for more numbers. And the tests need to be refactored as we go, just as aggressively as the production code.

So what?

To some, this might seem obvious and maybe simplistic. But its easy to miss subtleties when trying out the mechanical aspects of a new practice such as TDD – I’m sure I did something similar. Exploring a few blind alleys is a natural part of learning.

.