This post is quite simplistic about unit testing, but I hope it will make you try ! Simply put, an unit test is a piece of code that will test another piece of code. The "unit" part means that we will test an "unit" of our code, like a class and its methods. Unit tests are runned with NUnit (http://www.nunit.org).
Lets take a "simple" method :
public class StringUtil
{
public static string GetLength( string input )
{
return input.Length;
}
}
The test will look like this (add a reference to nunit.framework.dll) :
[TestFixture]
public class StringUtilTest
{
[Test]
public void GetLenghTest()
{
Assert.That( StringUtil.GetLength( "ab" ) == 2 );
Assert.That( StringUtil.GetLength( "abc" ) == 3 );
Assert.That( StringUtil.GetLength( "abcd" ) == 4 );
}
}
This test checks the internals of our method : given known input parameters ("abc"), we expect a specific output (3). Now, our method looks correct, and we have 100% coverage for it. But 100% coverage doesn't mean 0% bugs. Unit testing cannot test non existing code, like missing error checks, for example.
Now, an user submits a bug : "when I pass null to "GetLength", it fails with a NullReferenceException!". We now have two options : we return 0 if null is passed, or we throw an ArgumentNullException. We'll implement option 2. But before updating or GetLength method, we should add another test :
[TestFixture]
public class StringUtilTest
{
[Test]
public void GetLenghtTest()
{
Assert.That( StringUtil.GetLength( "ab" ) == 2 );
Assert.That( StringUtil.GetLength( "abc" ) == 3 );
Assert.That( StringUtil.GetLength( "abcd" ) == 4 );
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void GetLenghtArgNulTest
{
StringUtil.GetLength( null );
}
}
If we launch tests, "GetLengthArgNulTest" will fail, as we're expecting an ArgumentNullException, and we get a NullReferenceException. We leverage unit tests to reproduce the bug, before correcting it. It ensures that when the bug is corrected, it stays corrected. And we improve our state coverage.
public class StringUtil
{
public static string GetLength( string input )
{
if( input == null ) throw new ArgumentNullException( "Please dont feed me with null!");
return input.Length;
}
}
Everything is now green in NUnit!
Now, why are we writing this bunch of code, comment and maintain it ? For fiability, and future maintenance of our application : a modification in a portion of test covered code is much safer than the same modification in uncovered code.
In a multi layered application, you can test layers independantly. You don't need to go to all UI features to test your access layer. Finally, testable code is more modular, as modularity improves testability.