Saturday, November 10, 2007

Introduction
Test Drive Development (TDD) or unit testing are terms you may or may not have come across in the past, in this series of posts I’m going to look at TDD, I’ll give a brief overview and my arguments for TDD. In later posts I will begin to look more closely at the features available in Visual Studio 2008 to help you integrate TDD into your development process.

To start with lets look at the sport of rock climbing. As a climber climbs a route they will place ‘protection’ along the route. These are clipped onto the rope and rock face so that in the event  of a fall the ‘system’ catches the climber and all that is sustained is a bruised ego.  In contrast others will climb without any safety placed along the route, in the event of a fall in this scenario the outcome can be deadly.

The same is true for software development, TDD is your safety net, as your code evolves and new features are added or existing features extended you can move forward with confidence that your existing functionality has remained intact provided all the tests pass.  Be honest how many times have you been given the task of fixing a bug only to find the fix you have put in place has resulted in breaking another feature in the system? With TDD this situation can be avoided.

Some will make the argument that TDD means more effort and that makes it unfeasible in a commercial software development project. True there is a little more effort in writing the test code up front, however this is a one off, as the test code once completed should never have to change unless a business requirement changes. and having the tests in place creates a safety net that allows you to move forward with even more confidence, resulting in less time fixing features that have been broken by accident, therefore some of the investment in writing the tests can be reclaimed.

To successfully use TDD classes must be designed to do one thing and one thing only thus allowing them to be tested in complete isolation allowing you to pinpoint the location of bugs in your code. I’ve heard some tell me how this is impossible as this class relies on that classes and often a database is included in the mix. This is a fair point as systems are always comprised of multiple classes working together to accomplish the desired goal, but it should still be possible to isolate each class and test their functionality separately. A friend of mine once pointed out, if your classes are that tightly coupled that this is unachievable then your code isn’t written right. There’s a lot of truth to this statement and in these posts I’ll show some examples of how you can use simple techniques to isolate a class for testing.
 

The TDD Process
Essentially TDD is a very simple and straight forward process. You gather a set of requirements, for each requirement write one or more failing tests. After you have a set of failing tests you begin to write implementation code that will pass each of the tests. Once this code is completed and all the tests pass then you can be confident that the delivered code accurately meets the requirements.

You should avoid writing tests that do not have a corresponding requirement as this would lead to unnecessary implementation code being written. TDD should help you to focus on writing only the code that is required, and less code often means less bugs and less late nights.

It is possible to combine multiple classes together and test these, for example class A uses classes B and C. Here you could test class A and allow it to make the necessary calls to B and C if you want to achieve integration testing, however to get the true benefit of TDD you should at first test each of these classes in isolation as this will allow defects to be pinpointed to a specific class. All the examples that I use will focus to testing classes in isolation.


Getting Started With Visual Studio 2008
I am now going to introduce a simple example of creating a class library and a corresponding set of tests for the publicly exposed functionality. In this post I’m more concerned with introducing the techniques for creating and running unit tests than getting bogged down in a complex real world problem. In later posts I will introduce other more advanced features available in Visual Studio 2008 for TDD that will become more real world orientated such as testing an ASP.NET applications.


Show me the Code!!

For this example I’m keeping it very, very simple so that we focus on the techniques for writing a test and running it, this should take no more than fives minutes of your time to run through.

The scenario, you need to provide a class library that provides facilities to add, subtract and  multiply integer values. Told you it would be easy, so lets get started.

 1.     Start Visual Studio 2008

2.       Create a new blank solution called ‘MyFirstTDD’

3.     Add a new Test project call MathTests to the solution. Right click on the solution name in Solution Explorer and select “Add -> New Project …”. Delete UnitTest1.cs from the project.

4.   Add a new Class library to the solution called Math. Right click on the name of the solution in Solution Explorer and select “Add -> New Project …” Delete Class1.cs from the project.

5.   Now the test project will reference the classes in the actual class library, therefore you need to add a reference to the class library from the test project. To do this right click on the test project in Solution Explorer and select “Add Reference …”. Click the projects tab select Math and click ok.



6.   You now have the solution setup and we can being to write the code, test first of course.

7. Go to the test project, right click on the name and select Add -> New Test.. Name the test CalculatorTests.cs. This will generate a skeleton test class for the Calculator class that will be implemented later. For now pay attention to the TestClass and TestMethod attributes used at the class and method declarations respectively. The TestClass attribute signals that this class contains tests and the TestMethod attribute is used to define the individual tests. For methods marked with the TestMethod attribute to be considered valid tests they must be in a class that is marked with the TestClass attribute. For now remember when creating a test class you must mark it with the TestClass  attribute (Visual Studio will do this automatically when using the built in wizards to add your test class), also each method you wish to use as a test method must be marked with the TestMethod attrinbute.

8.     By default Visual Studio adds the method TestMethod1(), you can go ahead and delete this as we will be adding our own tests with more meaning full names.

9.     So now we start the actual development. Firstly we have to provide functionality to add, subtract and multiply integer values. This indicates that we should at the very least have three tests. So we will start by implementing a test that will validate that our Calculator class can add correctly.

10.  Open CalculatorTests.cs and add a new method called TestAddPositiveNumbers this method has a return type of void and takes no parameters, you should also mark this method with the TestMethod attribute.

   [TestMethod]
public void TestAddPositiveNumbers()
{
    Calculator cal = new Calculator();
    int result = cal.Add(2, 2);
    Assert.AreEqual(4, result);
}

11.  We will now implement the body of the test. To start with we will create an instance of out Calculator and call the Add method. We will get this test to ensure that two plus two always give us four. To do this we will use hard coded values, this is how you should code all your tests. Using hard coded values. It is important to use hard coded values as your tests must be conducted in a controlled environment where you know exactly what to expect from the methods you are testing.

12.  You can see from the code snippet that we simply create a new calculator and call the Add method passing two and two as parameters. Then we use the Assert class to check that the returned value matches four. The Assert class has a number of methods that can help with writing unit tests. Fort this post I’ll only be using the AreEquals method. This methods accepts two parameters and tests that they match, if they do not match then it will raise an exception and cause the test to fail, for now this is enough as I’ll look more closely at everything the Assert class provides in later posts.

13. Now we can try and compile the solution, which not surprisingly fails as we have not yet implemented our Calculator class. We will now define the calculator class but will only add enough code that allows the test class to compile and run, all tests should still fail.

14.  Right click on the Math project in solution explorer and select Add à Class ..

15.    Enter the name Calculator.cs and Add ok.

16.  Use the code snippet below to define the Add method for the Calculator. 

public int Add(int first, int second)
{
throw new NotImplementedException();
}

17.     The code should now compile.

18.    Having the code compile we can run the test at this point we expect the test to fail.

19.    To run the tests click Tests -> Run -> Run All Tests in Solution from the menu bar or use the keyboard shortcut Ctrl+R, A.

20. The tests will run and the test results window, shown below will indicate that your test has failed. At this time fails are good as we have not implemented any code, therefore if tests pass at this stage there would be little point in having them.


21.  Copy the code snippet below into CalculatorTests.cs compile and run the tests.

public void TestAddNegativeNumbers()
{
Calculator cal = new Calculator();
int result = cal.Add(-2, -2);
Assert.AreEqual(-4, result);
}

22.   Note that only one test has been run. This is because we have not added the TestMethod attribute to the TestAddNegativeNumbers method. Apply the attribute and run the tests again.

23.  We now have two failing tests.

24.  You should continue writing tests like this until all functionality has been tested. As this is only a simple blog we’ll skip out repeating these steps and implement the Add method and verify that both tests pass.

25.   Use the code snippet below to implement the add method.


public int Add(int first, int second)
{
    return first + second;
}

 26.     Compile the solution and run the test. In the test result window you can now see that the two tests have passed.

27.  This process of defining tests, verifying that they fail, implementing the code and verifying that the tests pass should continue until all the functionality has been implemented with supporting passing tests to verify it meets the requirements.


Conclusion
In this post I’ve defined the process for TDD and given a simple example of how to use TDD with Visual Studio 2008. In my next post I will look more closely at the nuts and bolts of the tools available in Visual Studio 2008 for implementing unit tests such as the various attributes and the methods available in the Assert class. 

If you would like to learn more about TDD I would recommend the book Test-Driven Development in Microsoft .NET by James W. Newkirk and Alexei A. Vorontsov. The examples in this book use NUnit but the techniques are applicable to Microsoft’s implementation of TDD.


Complete solution:
MyFirstTDD.zip (31.81 KB)