TDD test drive number 3

On two prior occasions, I have given TDD a test drive. Neither attempt convinced me that it was a useful way to design a system. To me, TDD has always stood for Top Down Design.

On the first attempt way back in 2006, I added a step to the Red Green Refactor cadence. I Redrew the design diagram before Green. My observation was that tests are no substitute for visualizing the solution.

On the second attempt in January 2009, I used TDD to design and construct a web navigation framework. It had three intersecting axes: URL mapping, security, and menu hierarchy. It ended up being extremely well tested, but incredibly complex. It worked, and continues to work, but it defies understanding.

Last month I began attending Dallas Geek Night at ThoughtWorks. This is a weekly meetup where open-source developers pair up and work on each other’s projects. I’ve used this opportunity to improve the tests for both Update Controls and Correspondence.

All of the work is done in pairs, and all of it is test driven.

Question and answer

On that first day, I paired with Richard Jensen, one of the organizers of the event. He asked questions about Update Controls. We answered those questions with tests.

For example, does Update Controls notify when something doesn’t actually change?

[TestMethod]
public void WhenSortOrderIsChangedButNotDifferentWeShouldNotGetACollectionChanged()
{
    ContactViewModel defaultOrder = _viewModel.Contacts.First();
    _viewModel.SortOrder = ContactListSortOrder.FirstName;

    ContactViewModel firstByFirstName = _viewModel.Contacts.First();
    _viewModel.SortOrder = ContactListSortOrder.FirstName;

    Assert.AreEqual(1, _collectionChangedCount);

}

At first this test failed. Then we added this code to make it pass:

public ContactListSortOrder SortOrder
{
    get
    {
        _indSortOrder.OnGet();
        return _sortOrder;
    }
    set
    {
        if (_sortOrder != value) _indSortOrder.OnSet();
        _sortOrder = value;
    }
}

I could have just answered the question, but this helped keep the discussion measureable. A simple answer would have just been based on faith, and could even have been wrong.

Finding bugs

On the second day, I paired with Dhruv Chandna. We were testing Correspondence synchronizing between two machines. He asked some good questions, too.

For example, what would happen if I added a fact to the opposite community:

[TestMethod]
public void WhenLogOnToOtherMachine_ShouldThrow()
{
    Machine playerOneMachine = _playerOneCommuniy.AddFact(new Machine());
    User playerOne = _playerOneCommuniy.AddFact(new User("one"));

    try
    {
        _playerTwoCommuniy.AddFact(new LogOn(playerOne, playerOneMachine));
        Assert.Fail("AddFact did not throw.");
    }
    catch (CorrespondenceException ex)
    {
        Assert.AreEqual("A fact cannot be added to a different community than its predecessors.", ex.Message);
    }
}

This error should have been caught. It was not. So after writing this failing test, Dhruv dug into the code base and fixed it.

foreach (RoleMemento role in prototype.PredecessorRoles)
{
    PredecessorBase predecessor = prototype.GetPredecessor(role);
    if (predecessor.Community != null && predecessor.Community != _community)
        throw new CorrespondenceException("A fact cannot be added to a different community than its predecessors.");
}

There were a few other changes required to make this work, but they did not significantly impact the design.

TDD as communication

Dan North says TDD is not about testing, it’s all about design. My experience tells me otherwise. Whenever I’ve let my design emerge through tests, I’ve ended up with an overly complex mess. It’s like pouring concrete into a mold. It takes the right shape because it is forced to. But the results are not as pretty as a sculpture.

Instead, I’m beginning to see TDD as a form of communication. This helps a pair to discuss a problem in tangible terms. It gives them something to point to. And it gives you a specific goal so that the conversation doesn’t get derailed. You are done when the test passes.

So I will continue to construct top-down designs. I will continue to use unit tests to verify my implementation of those designs. But when I pair, we will communicate through TDD.

Leave a Reply

You must be logged in to post a comment.