Why I love TDD


I just got back a project from a new vendor we are working with. I have to pass this project on to a client of ours, but first I want to make sure all of the .NET code is obfuscated. No big deal overall, as we expected this company was mid-level developers and I would have to dink it.
 
As expected, all of the code was contained in the asmx, so I decided I would start moving code to another project. I run through and figure out the lowest level item, which is to create a dictionary object for the string that will be sent to the webservice from a web app (don’t ask, it gets rather complicated. So, what do you do? Write a test. The code looks something like this:
 
[TestMethod()]
[
DeploymentItem("CompanyName.Client.dll")]
public void CreateDictionaryTest()
{
   
string stringtoBreakUp = "{string here}";
   
Dictionary<string, string> expected =
                              
TestHelper.GetExpectedDictionaryForTest();
   
Dictionary<string, string> actual =
                       
DataBaseHelper_Accessor.CreateDictionary(stringtoBreakUp );

    foreach (string key in actual.Keys)
    {
       
Assert.AreEqual(expected[key], actual[key], "Difference in value for key " + key);
    }
}

 
I run the test and it naturally fails (there is no implementation). So I implement the code to something like this (the original code was in VB, so I left it like that — just in case you wanted to know):
 

Private Shared Function CreateDictionary (ByVal str As String) _

                                          As Dictionary(Of String, String)

 

    ‘Using Generic Dictionary to make things easier (.NET 3.5)

    ‘BABELFISH: Usando el diccionario genérico para hacer cosas más fáciles (.NET 3.5)

    Dim dict As New Dictionary(Of String, String)

 

    ‘Array desde la cadena de Esns

    ‘ENGLISH: Array from the chain of Esns

    Dim arrEsn As String() = str.Split("|")

 

    ‘Add all items to a reusable dictionary

    ‘BABELFISH: Agregue todos los artículos a un diccionario reutilizable

    For i As Int32 = 0 To arrEsn.Length – 1

        ‘Descompongo clave, valor

        ‘*** Divide Hash into ESN and description

        Dim subArr As String() = arrEsn(i).Split("^")

 

        ‘Adding ESN and Description

        ‘BABELFISH: Adición de ESN y de la descripción

        dict.Add(subArr(0), subArr(1))

    Next

 

    Return dict

 End Function

 
The original routine made a rather complex SQL string and a hashtable. It was doing the work twice, so this is the first refactor. The reason for the BABELFISH comments is the developers here are offshore in South America. I am making sure they can read what I am doing, even if it is bad Spanish. Open-mouthed
 
The test now passes. This, of course is an easy test. Now, here are a list of tests I need to do to get to the end.
  1. Create a SQL String – This is just to be able to do things the way the developers did it and will be deprecated once I move to sprocs
  2. Get Vehicle List – This is a simple Get a DataSet test. And, yes, I am not using mocks at first, so this test will both altered and moved to an integration test harness
  3. Adorn the DataSet test – This one is to add the extra info sent on the original string that is ripped into a dictionary. While it is a bit crude, I prefer massaging the data up front to playing games when you bind
  4. Integration test of Getting a list of vehicles using the old signature (list string only)

In order to facilitate, I am going to move the ESN string to a TestHelper (not the actual name, but it is the purpose), so my test changes to this:

[TestMethod()]
[
DeploymentItem("CompanyName.Client.dll")]
public void CreateDictionaryTest()
{
   
string stringtoBreakUp = TestHelper.GetEsnString();

   
Dictionary<string, string> expected =
                              
TestHelper.GetExpectedDictionaryForTest();
   
Dictionary<string, string> actual =
                       
DataBaseHelper_Accessor.CreateDictionary(stringtoBreakUp );

    foreach (string key in actual.Keys)
    {
       
Assert.AreEqual(expected[key], actual[key], "Difference in value for key " + key);
    }
}

The change here is highlighted. Why move this to another class? I will need this same information over and over again. Looking at the test that gets a list from the database (the integration test), you see this:

string Esns = TestHelper.GetEsnString();
DataSet expected = TestHelper.GetDataSetOfVehiclesForTest();

 
It doesn’t get much easier to put the same data in each routine than to use a single method (code smell 101 – repeat code is BAD). By creating this helper class, I can feed each of my routines. But, it gets even sweeter. When I move the tests to an integration class, they can use the same helper. And, when I refactor to mocks, I already have a routine that helps me define what the mock should look like and deliver. Sweet!!!!
 
But, this is not even why I love TDD. When I got to my final test, which will likely remain an integration test, I went red after implementation. It delivers the exact same data set as the previous test, so it should not fail. But it did. In a non-TDD world, I would have searched for the problem for a long time, seeing the results of the issue on the UI. In TDD, I see that I have no data in the DataSet. I know the routine that grabs the data works, as it was just tested, so I can be confident the problem is in the current routine. A quick look at the routine reveals what I have not hooked  up correctly … and I do not even have to use the debugger to find it. Can life be sweeter than this.
 
If you have not been sold yet, you probably never will. But, I can’t sell all of the people all of the time.
 
Peace and Grace,
Greg

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: