In this article I introduce through a walkthrough the basics of how to write a good well structured unit test. This post is the first of a series about Unit testing and TDD. I’ll not be doing TDD for now because I first want to start with the basics of creating well structured unit tests. In later posts we will build on this to practice TDD. Therefor the tests of this first walkthrough will seem overly simple, but let’s crawls before we start running.
The details on how we should write tests vary with the programming framework & unit test framework. For this walkthrough I've choosen to implement the tests and examples witrh C# and VSTS .Nevertheless the core principle of how to implement automated tests stays applicable in any technology. If you desire to get the walkthroughs for NUnit or for other programming frameworks post a demand in this blog. You can download the completed walkthrough project here.
public class Fleet : IDisposable
{
private List _planes { get; set; }
public Fleet()
{
this._planes = new List();
}
public Plane this[string index]
{
get
{
return this._planes.Find(p => p.Name == index);
}
}
public void Add(string name)
{
this._planes.Add(new Plane(name));
}
public int Count
{
get
{
return this._planes.Count;
}
}
public void Dispose()
{
this._planes = null;
}
}
public class Plane
{
public Plane(string name)
{
this.Name = name;
}
public int PlaneID { get; set; }
public string Name { get; set; }
}
Examine the code of Fleet and Plane. The Fleet class represents an aero fleet (a simple Model) this class is a sort of collection of planes. The fleet class contains a property “Count” that counts the number of planes in the fleet.
Create a new Test Project File, right click solution, add, new, Project, select C#, Test, name your Project:”FirstStepsTest”.
Launch all tests: Ctrl R,A or Test, Windows, Test View
VS should launch the TestMethod1 and it should pass.
Replace the generated TestMethod() with the following test:
[TestMethod()]
public void NewFleet_WithNoPlanes_CountWillReturn0()
{
//Setup
Fleet fleet = new Fleet();
//Execute
int actual = fleet.Count;
//Verify
Assert.AreEqual(0, actual);
}
Run the test by right click on the test name and select “Run Test”.
The test above is a public method whose giving the attribute [TestMethod] the test class itself is been annotated with the attribute [TestClass]. This enable the testRunner of VSTS to retrieve all test methods.
Every test will always consist of 3 parts:
• The setup: to be able to test our SUT we’ve to put him in a certain state so it can be tested.
• The test execution: when our SUT is prepared to be test we can call the actual execution unit on our SUT.
• The verification: A test generally ends with the comparison on the expected results and the actual outcome of our execution.
The last part of our test uses an assertion method of our unit testing framework. Knowing what to assert about in your tests is pretty much 90% of the battle. An assertion is basically taking an expected value and comparing it to an actual value. If the expected value does not match the actual value, then the test will fail.
There are many other type of assertions but for simplicity and for readability when debuging I prefer to only use the Assert.Equals(). Assert equals is the most generic form of assertion and it will always display a meaningful message like Assert.AreEqual failed. Expected:. Actual:.Add following new tests to the test class:
[TestMethod()]
public void Add_OnePlane_WillIncrementCountByOne()
{
//Setup
Fleet fleet = new Fleet();
//Execute
fleet.Add("X2938");
//Verify
Assert.AreEqual(1, fleet.Count);
}
[TestMethod()]
public void RetriveByIndex_APlane_WillReturnThePlane()
{
//Setup
Fleet fleet = new Fleet();
fleet.Add("X2938");
//Execute
String actual = fleet["X2938"].Name;
//Verify
Assert.AreEqual("X2938", actual);
}
Examine the tests and run them, they should all pass.
We'll refactor a little bit our code because the test contains some parts of code that are repeated and repeating ourselves is always bad!
First we will remove the magic string: “X2938” and will instantiate a string field flightName containing this value. Replace “X238” with this new variable.
We will also promote the fleet object in each test to a new private field. We will use the [TestInitialize] attribute to instantiate this field.
Also imagine that our Fleet class implements IDisposable because it uses somehow an expensive resource that isn’t automatically disposed by the garbage collector. We could call fleet.Dispose() after the Assertion but what if our test fails? The dispose method would never been called and the object will remain in memory.
The result should look like that:
[TestClass]
public class FleetTest
{
private Fleet fleet;
const string flightName = "X2938";
[TestInitialize()]
public void TestInitialize()
{
fleet = new Fleet();
}
[TestCleanup()]
public void TearDown()
{
fleet.Dispose();
}
[TestMethod()]
public void NewFleet_WithNoPlanes_CountWillReturn0()
{
//Execute
int actual = fleet.Count;
//Verify
Assert.AreEqual(0, actual);
}
[TestMethod()]
public void Add_OnePlane_WillIncrementCountByOne()
{
//Execute
fleet.Add(flightName);
//Verify
Assert.AreEqual(1, fleet.Count);
}
[TestMethod()]
public void RetriveByIndex_APlane_WillReturnThePlane()
{
//Setup
fleet.Add(flightName);
//Execute
String actual = fleet[flightName].Name;
//Verify
Assert.AreEqual(flightName, actual);
}
}
We've defined a SetUp() and TearDown() pair of methods. In major XUnit frameworks, these are used to create Fixtures. A Fixture is some resource all tests in a test case have in common and which sets the context of the test (the situation in which we're testing), as well as other repetitive objects or resources.
VSTS uses the attributes [TestInitialize()] for the Setup, this part is run before each test and [TestCleanup()] for the teardown, run after each test.
Because each test must be isolated (cannot share information) these methods will tell the Framework to create our Fixture (a Fleet instance) before each test, and destroy it after. So each test gets a shiny new version to play with, free of anything a previous test may have done.
The test case above is a good small start and I hope you enjoyed this first walkthrough on unit testing, don’t hesitate to post some feedback!