RSS
Facebook
Twitter

Wednesday, April 07, 2010

The day I understood TDD

I’ve been practicing and advocating TDD (Test Driven Development) even before I’ve started working at Typemock but I can point at a specific point of time in which I actually “got it”.

At that time I had a great mentor and I was sure I got the whole “Red-Green-Refactor” routine, In fact I knew it so well that I allowed myself to “speed development” by writing the code before the actual test.

One day while happily coding with a fellow developer we came across an interesting problem: we needed to create a value generator – a class that will return a unique value each time a method (GetNextValue) is called.

Of course being two bright and talented developers we’ve started by debating how this class should be implemented and so that it would support every conceivable type – needless to say after a few minuets we were still “designing”  and every design we had was flawed – it had a bunch corner cases that forced us to search for yet another better-stronger-faster design.

Luckily for us we had someone in the same room that saw our plight and decided to put a stop to it. What he did is remind us how TDD should be done – one test at a time.

“Write a test that checks two unique integers” – he said.

“But it won’t work for strings or even doubles” – we said.

“Do it anyway” - And we did:

[TestMethod]

public void ValueGenerator_GenerateValuesForInt_TwoDifferentValuesReturned()

{

    var val1 = ValueGenerator.GetNextValue(typeof(int));

    var val2 = ValueGenerator.GetNextValue(typeof(int));

 

    Assert.AreNotEqual(val1, val2);

}

Making that test pass was simple:

internal class ValueGenerator

{

    private static int lastValue = 0;

 

    public int GetNextValue()

    {

        return lastValue++;

    }

 

    public void Reset()

    {

        lastValue = 0;

    }

}

Not very impressive is it. We’ve decided to write another test, this time using double:

[TestMethod]

public void ValueGenerator_GenerateValuesForDouble_TwoDifferentValuesReturned()

{

    var val1 = ValueGenerator.GetNextValue(typeof(double));

    var val2 = ValueGenerator.GetNextValue(typeof(double));

 

    Assert.AreNotEqual(val1, val2);

}

After implementing the code for double we’ve decided to refactor and created a new class to handle all “numerical values”:

internal class DiscreteNumberValueGenerator

{

    private static byte lastValue = 0;

 

    public object GetNextValue()

    {

        return lastValue++;

    }

 

    public void Reset()

    {

        lastValue = 0;

    }

}

We’ve also added tests and implementations for string, DateTime and boolean values and after a refactoring the value generator looks something like this:

static ValueGenerator()

{

    var discreteNumberValueGenerator = new DiscreteNumberValueGenerator();

    generators = new Dictionary<Type, IValueGenerator>

                     {

                         {typeof (int),         discreteNumberValueGenerator},

                         {typeof (double),      discreteNumberValueGenerator},

                         {typeof (float),       discreteNumberValueGenerator},

                         {typeof (long),        discreteNumberValueGenerator},

                         {typeof (ulong),       discreteNumberValueGenerator},

                         {typeof (short),       discreteNumberValueGenerator},

                         {typeof (ushort),      discreteNumberValueGenerator},

                         {typeof (byte),        discreteNumberValueGenerator},

                         {typeof (sbyte),       discreteNumberValueGenerator},

                         {typeof (uint),        discreteNumberValueGenerator},

                         {typeof (char),        discreteNumberValueGenerator},

                         {typeof (decimal),        discreteNumberValueGenerator},

                         {typeof (DateTime),    new DateTimeValueGenerator()},

                         {typeof (string),      new StringValueGenerator()},

                         {typeof (bool),        new BooleanValueGenerator()}

                     };

}

 

internal static void ResetGenerators()

{

    foreach (var generator in generators.Values)

    {

        generator.Reset();

    }

}

 

internal static object GetNextValue(Type type)

{

    object ret = null;

    var isNonConcreteType = type.IsInterface || type.IsAbstract;

 

    if (generators.ContainsKey(type))

    {

        ret = generators[type].GetNextValue();

        return Convert.ChangeType(ret, type);

    }

 

    if (type.IsValueType)

    {

        ret = GenerateForValueType(type);

        return Convert.ChangeType(ret, type);

    }

 

    if (isNonConcreteType)

    {

    ...

We didn’t write whole of the code upfront - instead of trying to solve a big problem we’ve solves small simple problems written as tests until we had a fully working solution.

 

Since that day I follow TDD religiously – one test at a time.




2 comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...