2

Test Driven Development (TDD) is the amalgam of two programming techniques: Test-First Development (TFD) and Refactoring. Looking at the first of these, TFD literally means that before you write some functional code (in whatever programming language you happen to favor) you first write a small piece of code to test the outcome or result of that functional code which doesn't yet exist. It might sound bizarre, testing something that doesn't yet exist, but that is exactly the point: you want your test to fail. Don’t worry, all will become clear by the end of the tutorial!

So why might you want to use TDD at all? Well firstly you need to get into a different mindset. Consider this from ‘Object thinking’ by David West: “a good developer should learn to concentrate on the problem space and not leap instinctively into the solution space.”

Take this example:

Problem: extract the UK postal area code from any given full UK postcode. Example Input “SS17 7HN", output should be “SS", for input “B43 4RW", output should be “B"

Instinctively you are probably already declaring a string variable in your head. But a better approach to analyzing this problem is to identify the objects and their responsibility, the result of this discipline will help produce the solution itself. There is obviously a Postcode object required and it should have the responsibility to output it's postal area. Objects equal classes and responsibilities are their methods, so the solution will involve a postcode class with a postalArea() method.

So your test will contain something like (assuming c#):

Postcode objPc = new Postcode();
string strPa = objPc.postalArea();

All you need now is a unit testing framework to put your test in. So how do you actually do it then? Let us look at an example using SharpDevelop and Nunit which integrate nicely. At its most basic, Nunit is a DLL assembly that can be referenced in your project. OK, so open SharpDevelop and click the 'New Solution' button on the start page. Select the Class Library template and name it Postcode. Click OK.

A new solution and project is created with a class file MyClass.cs. Set your namespace if necessary (usually company or personal name and then any subgrouping if applicable) Rename the class 'Postcode'. Expand the project in the solution explorer on the left hand side and rename the class file 'postcode.cs'. Right click on references in the solution explorer and choose Add Reference. Scroll down to the nunit.framework entry in the list of references in the GAC tab. This is assuming that you downloaded the nunit msi installer from [URL]http://www.nunit.org[/URL] and installed it.
Right click the project on the solution explorer and choose Add->Item, in the dialogue box that appears choose 'Unit Test' and name it 'PostcodeTest.cs'

Right click the project on the solution explorer and choose Add->Item, in the dialogue box that appears choose 'Unit Test' and name it 'PostcodeTest.cs' after which you should get a new file in the solution explorer 'PostcodeTest.cs' and then rename the namespace if necessary. Note the [TestFixture] and [Test] attributes, see the 'Getting Started' documentation for the 2.2.7 release of Nunit where these attributes are explained.

Now we are ready to demonstrate test first development. With an expectation of the class being able to return the part of a UK postcode that represents its Postal Area in this case, add some code to the test class to exercise the real class in this manner. In the 'PostcodeTest.cs' file add the following lines of code:

using System;
using NUnit.Framework;

namespace Gadgitbox.Business.Tests
{
    [TestFixture]
    public class PostcodeTest
    {
        [Test]
        public void postalArea()
        {
            // TODO: Add your test.
            string[] strPc = {"SS17 7HN", "B43 4RW"};
            string[] strPa = {"SS", "B"};

            Postcode[] arrPc = new Postcode[strPc.Length];

            for(int i = 0; i < strPc.Length; i++){

                arrPc[i] = new Postcode(strPc[i]);
                Assert.AreEqual(strPa[i], arrPc[i].postalArea());
            }

        }
    }
}

How did you know to write all that? Easy it's all in the problem. Look back at the problem statement earlier in this article, there were two postcodes in it and two corresponding postal areas. They are the sample data so you need a couple of string arrays to hold those, a common method to hold small amounts of data in memory. There are two postcodes in the problem so that’s multiple instances of a postcode object required. Therefore, you might as well have those in an array too. To test each possibility in the problem you need to iterate through them so you set up a for loop. In there you can instantiate an instance of class postcode for each example and call its postalArea() method. The Assert class and the AreEqual() method are provided in the Nunit framework that you added as a reference to the project, as are the [TestFixture] and [Test] attributes.

So go ahead press F8 or select Build Solution from the build menu. You should get two build errors. Good the test fails that's what you want the first time around. If you have more than two errors check you typed in the syntax correctly.

The first of the build errors will tell you there is no overload for method Postcode, so you need a constructor with a string parameter to accept the postcode (see how the solution is emerging of itself.) The second compiler error tells you that the postalArea() method doesn't exist so you need to create that too. So switch to Postcode.cs and amend the code so it looks like this:

public class Postcode
{
    public Postcode(string strPc)
    {

    }

    public string postalArea()
    {
        return "postcode";
    }
}

Bear in mind the following anecdotes, both of which amount to the same thing so choose which you find most memorable or better still remember both!

  • “A system should be as simple as possible, but never simpler" - Albert Einstein.
  • K.I.S.S. (Keep It Simple Stupid) The goal here is to write as little functional code as possible to satisfy the test, by doing this we are not only keeping things as simple as possible but we are following another discipline that recommends you code in small increments ( a few lines of code at a time as opposed to 200+ lines of code per build).

Ok time for F8 again, this time the project should build. Now it's time to run the unit test. Fire up the Nunit GUI from the shortcut added to your start->programs menu when you installed Nunit. From the File menu choose open and browse to the dll file SharpDevelop has just built. Once it has loaded click the “Run" button. A sea of red should meet your eyes as it's not home time yet. But look how informative the error is, you can see the length and output is not as expected. Not only does it tell you the difference is at index 0 it puts a little ASCII hat symbol showing you where index 0 is!

What do you need to pass this test? Easy, you need a private string member variable to hold the postcode passed in to the constructor, and some logic in the postalArea() method to extract the postal area from it. Amend Postcode.cs as follows:

public class Postcode
{
    private string m_strPostcode;

    public Postcode(string strPc)
    {
        m_strPostcode = strPc;    
    }

    public string postalArea()
    {
        return m_strPostcode.Substring(1,2);
    }
}

Build the solution and from the file menu in Nunit GUI choose 'Reload.' It still fails? A common mistake in the parameters of the substring method, it should be from index 0 not 1 (see how easy these common developer errors are picked up, without your unit test how long might you have been looking for that with 200 lines of code between builds?)

Right easy then, replace the 1 with a 0 thus:

return m_strPostcode.Substring(0,2);

And build, reload, run - see how this is getting iterative?

Still fails! On the second postcode substring is not enough, you are still too simple, you need to cope with the fact some UK postcodes have a single character to identify them. Amend your code as follows:

public string postalArea()
{
    if (m_strPostcode.Length < 8){
        return m_strPostcode.Substring(0,1)
    }else{
        return m_strPostcode.Substring(0,2);
    }
}

A sea of green? Excellent your job is done you can deploy and go home and sleep soundly knowing that support call will not come. Oh but you do get a support call? Curses! What postcode was the customer using? “W1Y2 3RD" Tsk, the Postal Service obviously haven't heard of KISS either. Anyway it's no problem, first thing next morning open up your project and add “W1Y2 3RD" and the expected area “W" for West London to your test.

string[] strPc = {"SS17 7HN", "B43 4RW", "W1Y2 3RD"};
string[] strPa = {"SS", "B", "W"};

Your logic error is now exposed, your implementation is still a little on the simple side and you need to take a different approach. Rather than the length of the postcode you need to look at the second character and test if it's a number or a letter. Add the following private method to your Postcode class:

private static bool IsNumeric(string stringToTest)

{

    double newVal;

    return double.TryParse(stringToTest, NumberStyles.Any, 
                          NumberFormatInfo.InvariantInfo, 
                          out newVal);

}

Now amend the if statement in the postalArea() method like this:

if (IsNumeric(m_strPostcode.Substring(1,1))){

Build, Reload, Run. The tests should now pass. So TDD may initially seem like hard work, but it really can save you time and frustration further down the line. This tutorial has only scratched the surface when it comes to the advantages of developing your applications in this way. So here are some recommendations for further reading and useful links:

Kent Beck and Cynthia Andres: Extreme Programming Explained: Embrace Change, Second Edition, Addison-Wesley, ISBN 0321278658

David West: Object Thinking, Microsoft Press, ISBN 0735619654

http://www.agiledata.org/essays/tdd.html
http://www.nunit.org
http://www.sharpdevelop.net/OpenSource/SD/Default.aspx

Edited by Reverend Jim: Fixed formatting

Votes + Comments
Thanks for the intro to test driven dev. I wish there were more tutorials like this on daniweb!
3
Contributors
2
Replies
15
Views
11 Years
Discussion Span
Last Post by jeryraza
0

Thank you for your post. A little question : should you add a test for the static method IsNumeric ?

This topic has been dead for over six months. Start a new discussion instead.
Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.