Saturday, October 30, 2010

Training big refactorings with code katas

Recently, we had to refactor some methods out of a class into a new class. In the past, I already made some big refactorings saved by unit tests. Retrospectively, I must admit that my unit tests back then weren't unit tests but mainly integration tests: although I stubbed away the serial communication, my tests used always all layers of the program. So, instead of mocking C when testing D-->C (uses), I mocked A in D-->C-->B-->A. That means, if you want to refactor B in this chain to B'-->B'', you have to adjust all tests of C and D respectively. This is of course cumbersome.
Thank god, I almost always mock the next layer now, so that refactoring the tests is manageable. I even succeeded to refactor the tests step by step in a TDD-manner having mostly only 1 red test. To be able to do that, the Bank OCR-kata has helped me a lot. In this kata, you can do two class-refactor-steps in the first user story. I would like to present the second one in respect to clearly refactor the tests also (it helps a lot if you're familiar with this kata, so feel free to dive into it or even do a test-run for your own before reading further on).
Code so far:
public class BankAccountParser
{
  private readonly IBankAccountSplitter _bankAccountSplitter;

  public BankAccountParser(IBankAccountSplitter bankAccountSplitter)
  {
    if (bankAccountSplitter == null)
      throw new ArgumentNullException("bankAccountSplitter");

    _bankAccountSplitter = bankAccountSplitter;
  }

  public string Parse(string bankAccount)
  {
    if (string.IsNullOrEmpty(bankAccount))
      throw new ArgumentNullException("bankAccount");

    var bankAccountNumber = new StringBuilder();

    foreach (var digit in _bankAccountSplitter.Split(bankAccount))
    {
      if (digit == " _ | ||_|")
      {
        bankAccountNumber.Append("0");
      }
      else
      {
        bankAccountNumber.Append("1");
      }
    }

    return bankAccountNumber.ToString();
  }
}
Tests so far:
[TestFixture]
public class BankAccountParserTests
{
  private BankAccountParser _target;
  private IBankAccountSplitter _bankAccountSplitter;

  [SetUp]
  public void Setup()
  {
    _bankAccountSplitter = MockRepository.GenerateStub<IBankAccountSplitter>();
    _target = new BankAccountParser(_bankAccountSplitter);
  }

  [Test]
  public void Ctor_ObjectIsNotNull()
  {
    Assert.IsNotNull(new BankAccountParser(_bankAccountSplitter,
MockRepository.GenerateStub<IDigitParser>()));
  }

  [Test]
  public void Ctor_BankAccountSplitterIsNull_ThrowArgumentNullException()
  {
    Assert.Throws<ArgumentNullException>(() => new BankAccountParser(null));
  }

  [Test]
  public void Parse_Null_ThrowsArgumentNullException()
  {
    Assert.Throws<ArgumentNullException>(() => _target.Parse(null));
  }

  [Test]
  public void Parse_Zeros()
  {
    var zeros = " _  _  _  _  _  _  _  _  _ \n" +
                "| || || || || || || || || |\n" +
                "|_||_||_||_||_||_||_||_||_|\n" +
                "                           \n";

    _bankAccountSplitter.Stub(x => x.Split(zeros)).Return(new List<String>
    {
      " _ | ||_|",
      " _ | ||_|",
      " _ | ||_|",
      " _ | ||_|",
      " _ | ||_|",
      " _ | ||_|",
      " _ | ||_|",
      " _ | ||_|",
      " _ | ||_|"
    });

    var actual = _target.Parse(zeros);

    Assert.That(actual, Is.EqualTo("000000000"));
  }

  [Test]
  public void Parse_AOneAndZeros()
  {
    var oneAndZeros = "    _  _  _  _  _  _  _  _ \n" +
                      "  || || || || || || || || |\n" +
                      "  ||_||_||_||_||_||_||_||_|\n" +
                      "                           \n";

    _bankAccountSplitter.Stub(x => x.Split(oneAndZeros)).Return(new List<String>
    {
      "     |  |",
      " _ | ||_|",
      " _ | ||_|",
      " _ | ||_|",
      " _ | ||_|",
      " _ | ||_|",
      " _ | ||_|",
      " _ | ||_|",
      " _ | ||_|"
    });

    var actual = _target.Parse(oneAndZeros);

    Assert.That(actual, Is.EqualTo("100000000"));
  }
}
Now I would like to introduce a DigitParser-class which should parse one digit and return a 1-string-representation of it. Therefore, I introduce the interface for it:
public interface IDigitParser
{
  string Parse(string bankAccountDigit);
}
Next, I need to inject it into the target. To do that, I take the easiest test and rewrite it using the new interface:
[Test]
public void Ctor_ObjectIsNotNull()
{
  Assert.IsNotNull(new BankAccountParser(_bankAccountSplitter,
                                         MockRepository.GenerateStub<IDigitParser>()));
}
To make it green, just complete the constructor:
public BankAccountParser(IBankAccountSplitter bankAccountSplitter, IDigitParser digitParser)
{
  if (bankAccountSplitter == null)
    throw new ArgumentNullException("bankAccountSplitter");

  _bankAccountSplitter = bankAccountSplitter;
}
This makes all other tests red because they do not compile anymore. So, alter the setup-method
[SetUp]
public void Setup()
{
  _bankAccountSplitter = MockRepository.GenerateStub<IBankAccountSplitter>();
  _digitParser = MockRepository.GenerateStub<IDigitParser>();

  _target = new BankAccountParser(_bankAccountSplitter, _digitParser);
}
and one test:
[Test]
public void Ctor_BankAccountSplitterIsNull_ThrowArgumentNullException()
{
  Assert.Throws<ArgumentNullException>(() =>
    new BankAccountParser(null, MockRepository.GenerateStub<IDigitParser>()));
}
We're green again and ready for a next test: Assert that when passing in a null-DigitParser, an ArgumentNullException will be thrown.
[Test]
public void Ctor_DigitParserIsNull_ThrowArgumentNullException()
{
  Assert.Throws<ArgumentNullException>(() =>
    new BankAccountParser(_bankAccountSplitter, null));
}
This test is red and becomes green with following code:
public BankAccountParser(IBankAccountSplitter bankAccountSplitter, IDigitParser digitParser)
{
  if (bankAccountSplitter == null)
    throw new ArgumentNullException("bankAccountSplitter");

  if (digitParser == null)
    throw new ArgumentNullException("digitParser");

  _bankAccountSplitter = bankAccountSplitter;
  _digitParser = digitParser;
}
Now comes an interesting step, because we insert an hint into one test how to refactor the sut:
[Test]
public void Parse_Zeros()
{
  var zeros = " _  _  _  _  _  _  _  _  _ \n" +
              "| || || || || || || || || |\n" +
              "|_||_||_||_||_||_||_||_||_|\n" +
              "                           \n";

  _bankAccountSplitter.Stub(x => x.Split(zeros)).Return(new List<string>
  {
    " _ | ||_|",
    " _ | ||_|",
    " _ | ||_|",
    " _ | ||_|",
    " _ | ||_|",
    " _ | ||_|",
    " _ | ||_|",
    " _ | ||_|",
    " _ | ||_|",
  });
  _digitParser.Stub(x => x.Parse(" _ | ||_|")).Return("0");

  var actual = _target.Parse(zeros);

  Assert.That(actual, Is.EqualTo("000000000"));
}
All tests are still green, so we're ready to refactor:
public string Parse(string bankAccount)
{
  if (string.IsNullOrEmpty(bankAccount))
    throw new ArgumentNullException("bankAccount");

  var bankAccountNumber = new StringBuilder();

  foreach (var digit in _bankAccountSplitter.Split(bankAccount))
  {
    if (!string.IsNullOrEmpty(_digitParser.Parse(digit)))
    {
      bankAccountNumber.Append(_digitParser.Parse(digit));
    }
    else
    {
      if (digit == " _ | ||_|")
      {
        bankAccountNumber.Append("0");
      }
      else
      {
        bankAccountNumber.Append("1");
      }
    }
  }

  return bankAccountNumber.ToString();
}
Great, but this can be rewritten easier:
public string Parse(string bankAccount)
{
  if (string.IsNullOrEmpty(bankAccount))
    throw new ArgumentNullException("bankAccount");

  var bankAccountNumber = new StringBuilder();

  foreach (var digit in _bankAccountSplitter.Split(bankAccount))
  {
    bankAccountNumber.Append(string.IsNullOrEmpty(_digitParser.Parse(digit)) ?
      "1" :
      _digitParser.Parse(digit));
  }

  return bankAccountNumber.ToString();
}
But now, the last test is red. We haven't stubbed the DigitParser there yet. To do this, I move the stubbing into the setup-method and also add the canned answer needed for the last test:
[SetUp]
public void Setup()
{
  _bankAccountSplitter = MockRepository.GenerateStub<IBankAccountSplitter>();

  _digitParser = MockRepository.GenerateStub<IDigitParser>();
  _digitParser.Stub(x => x.Parse(" _ | ||_|")).Return("0");
  _digitParser.Stub(x => x.Parse("     |  |")).Return("1");

  _target = new BankAccountParser(_bankAccountSplitter, _digitParser);
}
Prepared like this, we tweak the production code a last time ending with
public string Parse(string bankAccount)
{
  if (string.IsNullOrEmpty(bankAccount))
    throw new ArgumentNullException("bankAccount");

  var bankAccountNumber = new StringBuilder();

  foreach (var digit in _bankAccountSplitter.Split(bankAccount))
  {
    bankAccountNumber.Append( _digitParser.Parse(digit));
  }

  return bankAccountNumber.ToString();
}
But this is not the end for the tests. Actually, we're just testing if the BankAccountSplitter and the DigitParser are working together correctly. So, instead of the last two tests, we should write:
[Test]
public void Parse_SplittedInto2Digits_ConcatenateDigits()
{
  _bankAccountSplitter.Stub(x => x.Split(null)).
    IgnoreArguments().
    Return(new List<string>
    {
      "foo",
      "bar"
    });

  _digitParser.Stub(x => x.Parse("foo")).Return("a");
  _digitParser.Stub(x => x.Parse("bar")).Return("b");

  var actual = _target.Parse("nonrelevant");

  Assert.That(actual, Is.EqualTo("ab"));
}
This points us to some more tests, I had forgotten initially like what happens when the DigitParser returns null or throws an error? What if the BankAccountSplitter has this behavior?

This scales pretty well to "real life" code and as said before, the training with this kata has helped me recognize this pattern during my work and the usefulness of it.

Thursday, October 7, 2010

Coding dojo helper - Mortal Kombat Edition

A colleague of mine told us that really good developers could do a red-green-refactor-circle in about 90 seconds. This sounds quite fast, but I thought that maybe one day we could achieve that... To help getting a feeling for the time spend doing such a circle, I wrote a tool. This tool should take the time of each developer doing a round and calculate the average time of a complete timebox used for a kata, for instance.

Learned new tools :)

As a kata originally comes from martial arts, the tool got a touch of the famous video game Mortal Kombat ^^ For development, I used Blend, Gimp and Audacity to do the design and learned some new tricks. For example, one can draw an ellipse with Blend and select Object --> Path to convert it into an animation path. This path can be used in a storyboard to make a button go round another control for example:
The second thing I've learned is to capture keystrokes even if the window isn't active. This article of Stephen Toub has shown me how to do that. So, if the user presses the Scroll-Button, the stopwatch starts. If he has finished his work, he presses the Scroll-Button again and the next developer begins to code. At the end of the timebox, one presses the Pause-Button and the screen changes to show the average TDD-time and a chart listing all times:
Neat, he? This is also the first time I used a DCVS.


The first attempt was to use Git, GitHub and TortoiseGIT as mediator. But as I'm developing this on a company-laptop during my ride home, there were some difficulties accessing GitHub and I abandoned this quite fast (I do think there are ways to circumvent this, but I hadn't got the nerves to look for them any further). So I tried Mercurial, Kiln and TortoiseHg and it worked like a charm. If you want, you can fetch the sourcecode at codingdojo.kilnhg.com and have a try for yourself if you can master the red-green-refactor-mantra in under 90 seconds. On our first try, we set the alarm to a moderate 3-min-timeout, but did a 4:30 min in average *ouch*. Next time, we'll use a 4-min-timeout and are keen to beat that :)