Test Driven Development (TDD) is one of the practices among a collection of practices within the Agile methodology.
Note: The word Agile is a definition of “moving quickly and lightly” and Agile is a methodology that is based on several principals and methodologies. The idea of Agile Development is to build software quickly, in the face of rapidly changing requirements.
One of the principles behind the Agile Manifesto is:
Our highest priority is to satisfy the customer through early and continuous delivery of valuable software. http://agilemanifesto.org/
To meet this principle we have to satisfy the customer with a product that will work, that has high quality and does not have bugs (Does such software exist!?). With TDD we can increase the quality of software and make sure that bugs don’t slip through when we are developing our software. As you may know even the smallest bug can get through a door sprayed with anti bug spray. So TDD will not make the software 100% water proof, but it will definitely reduce the number of bugs and increase the quality of the software.
TDD is used through the whole development stage in a project and TDD will become more controlled (easy to handle) if the software is split into small development iterations, where an iteration can be based on a use case (similar to an Agile user story).
When using TDD, we design our test before doing any implementation. Maybe this sounds backwards, but the effect of writing tests first forces us to think about how a caller of our program will interact with our code. This is a big help in designing clean and useful code. By writing the test first will also force our self to design the program to be testable, and the idea of TDD is to test everything we write and make sure everything works. It’s only through testing we know that our program will behave in the way we are expecting it too do. If we don’t test our program, we are probably going to fail to satisfy the customer. Another great benefit of writing our test is that the test itself can work as documentation over how to use the program.
An example of Test-First Design
Assume that we are going to implement a class used as a collection of items. We know that we will add some items and that we can get the number of items added to the class. Before you implement this class we write the test:
[TestMethod]
public void AddItemsTest()
{
MyClass<int> myClass = new MyClass<int>();
myClass.AddItem(1);
myClass.AddItem(2);
myClass.AddItem(3);
Assert.Equals(3, myClass.Count);
}
When we compile the above code, the compilation will fail, because MyClass is not yet implemented. So what we just did is a Test-First Design. To make this test passed we have to implement MyClass and its AddItem and Count members:
public class MyClass<T>
{
private List<T> _items;
public MyClass()
{
_items = new List<T>();
}
public void AddItem(T item)
{
}
public int Count
{
get { return 0; }
}
}
After MyClass is implemented the compilation will pass if everything is implemented correctly. When we run our test, the test will not pass (When we build a test we should strive for making the test to fail before we make it pass). The test we had created will check if the Count property of MyClass returns 3 and in the code above it will return 0. All logic for the members of MyClass is not yet implemented, so we had to add the logic to the members and run the test again.
public class MyClass<T>
{
private List<T> _items;
public MyClass()
{
_items = new List<T>();
}
public void AddItem(T item)
{
this._items.Add(item);
}
public int Count
{
get { return this._item.Count; }
}
}
When the test is running after the logic of the members are added, the test will pass.
Now when we have a working test we want to add a new method that will get the first item added to the MyClass. Before we add the new method we create a new test. The test will check if FirstItem is equal to 1:
[TestMethod]
public void GetFirstItemTest()
{
MyClass<int> myClass = new MyClass<int>();
int i = myClass.FirstItem;
Assert.Equals(1, i);
}
After the test is written we compile the code. The compilation will fail because the FirstItem property is not added to MyClass yet. So we add the FirstItem property to MyClass and recompile the code, and if everything goes well we run the test.
public T FirstItem
{
get { return this._items[0]; }
}
This time the test will not pass. An Index out of range exception will be thrown, because there are no items added to MyClass.
Note: The test fails because it was not correct written.
We modify the test and add an item “1” to the test and run it again:
[TestMethod]
public void GetFirstItemTest ()
{
MyClass<int> myClass = new MyClass<int>();
myClass.AddItem(1);
int i = myClass.FirstItem;
Assert.Equals(1, i);
}
This time the test will pass.
When we create test, we maybe don’t want to instantiate the same class in every test method as we have done so far. Instead we can use an Initialize method that will be called before each of ours tests is called. This is done by creating a method and marks it with the TestInitializeAttribute (An explanation of the tool I use for the test will be found later in this article):
private MyClass<int> myClass;
[TestInitialize()]
public void Initialize()
{
myClass = new MyClass<int>();
}
The example above will create an instance of MyClass. When this is done we can modify our other test to use the private field myClass:
[TestMethod]
public void AddItemsTest()
{
myClass.AddItem(1);
myClass.AddItem(2);
myClass.AddItem(2);
Assert.Equals(3, myClass.Count);
}
[TestMethod]
public void GetFirstItemTest ()
{
int i = this.myClass.FirstItem;
Assert.Equals(1, i);
}
Information about the test code used in this article.
In this article I have used Visual Studio 2005 Team System’s test project and “N Unit” classes to make the test cases. When creating a test case with the Team System, we first need to create a test project and create an ”N Unit class” (The code in this article is from a created N Unit class) for each test case. To run a test with the Team System, we use the Test Explorer. The Test Explorer will list all methods mark with the TestMethodAttribute (Those methods are the test we have written). We can select which method(s) we want to test before we run the test case. When a test is running the method marked with the TestInitializeAttribute will be executed before each method mark with the TestMethodAttribute.Before a test method ends, the method marked with the TestCleanupAttribute will be called. If we want to clean up after a test method is done we can use a Cleanup method:
[TestCleanup()]
public void Cleanup()
{
// TODO: Add test cleanup code
}
The whole process of first writing the test before any code is called “intentional programming”. We state our intent in a test before we implement it. We make our intent as simple as possible and readable as possible, it will make it easer for us to create a good structure of our program and make good design decisions.
If you are interested of reading more about TDD, I would recommend the Kent Beck’s TDD book:
Test Driven Development: By Example, ISBN: 0321146530
I want to thank Robert Jeppesen and Jimmy Nilsson for the review of this article.