If you’re developing an application to track your own DVD collection, you can afford to just dive in and start writing code without worrying about the process. However, if you’re writing an application to manage the process of scheduling labor and ordering raw materials to produce DVDs for a consulting client, you can’t afford to be so slapdash about things. One of the keys to producing robust software for a client is having a robust and repeatable process for creating that software.
Test-first development is a natural part of such a robust process. Test-first development, which is one of a group of practices collectively known as extreme programming, or XP, relies on running miniature tests, or unit tests, one by one to fix specific code problems. In this article, I’ll look at how to use the open source tool NUnit to make the most of this unit-test process in .NET applications.
How test-first development works
Like in-house coders, consultants with development skills are familiar with the typical model of the software development process: architect and design the product; write the code; test the results; fix any bugs until the testing department is happy; release the product, sell lots of copies, and retire. Seems straightforward enough.
But what if you turned that process on its head? That’s just what happens with test-first development. Here’s how it works:
The only reason to write code is because something is missing from your application. For example, the program requirements might include retrieving a list of all computers in the current network domain. Rather than charging into code, you start by writing the test that would work if the code existed: in this case, returning a list of the computers that you already know are in your test domain. Run the test, and—not surprisingly–it fails. Then you can start writing code. When the code passes the test you wrote, you think about the next thing it should be able to do, and write that test. When the code passes all of the tests that you can think of, you’re done.
Each of these tests is called a unit test. Instead of throwing unit tests away after writing them, you accumulate them into a test suite. Each time you write new code, you run not only the immediately applicable unit test but also all of the unit tests in the entire test suite. At any given time, at most one test (the one for the functionality that you’re currently implementing) should fail. If other unit tests start failing, it’s time to back up and figure out what you did wrong.
There are a variety of tools out there designed to support unit testing and test-first development in .NET applications. These include .NETUnit, X-Unity, and the product I use myself, NUnit. I like NUnit because it’s open source and free; it’s being actively developed, and it meshes well with .NET.
Unit tests in NUnit are nothing more than methods in a class. The key is that these methods and classes are decorated with custom attributes. For example, Listing A is a tiny C# test suite consisting of a single test.
The TestFixture attribute identifies this class to NUnit as a class that contains tests. The Test attribute identifies the CreateTransformer method as a unit test. It’s a very simple test: All it does is make sure that a new instance of a particular class can be created without raising an exception.
NUnit tests can also check the results of an operation. They do this with the NUnit Assertion object. Here’s an example:
public void CheckEncoding()
HtmlTransform ht = new HtmlTransform();
string result = HtmlTransformer.Apply(@"<x>foo<x>", ht);
Assertion.Assert(result, result == @"<x>foo<x>");
For this test to be considered successful, the result has to equal the literal string specified in the Assertion.Assert method. If it doesn’t, NUnit returns the first parameter (here, the actual result) to its user interface so that you can tell which test failed and how it failed. Other checks you can make include whether two objects are the same, whether a particular exception was thrown, or whether two numbers are within a specified delta from one another.
It doesn’t matter whether you keep your tests in the same assembly as the actual code or in a completely different project. You don't need to do anything beyond adding the attributes to tell NUnit about your tests. NUnit uses .NET reflection to find tests; all you have to do is tell it which assembly to look in. Reflection is the capability that lets .NET code retrieve information about other .NET code. NUnit uses reflection to find, and then execute, all of the tests in a particular assembly.
There are several ways to actually execute NUnit tests. One is via the graphical test runner shown in Figure A.
In this case, the green dots show successful tests; you can see how the division of tests into classes produces a natural hierarchy. The yellow progress bar indicates that I’ve temporarily set one of the tests in my test suite to be ignored because I’m working on it. (NUnit won’t show you all green unless every test is active and passed.)
A red dot or bar would indicate a failing test. You can see that running the entire test suite of more than 300 tests took a bit less than three seconds on my development computer. Tiny tests are generally fast to execute.
NUnit also has a command line version that lets you invoke a test suite without bringing up the graphical interface. This is ideal when you’re using NUnit as part of an automated build process. (I use the excellent FinalBuilder, but there are other alternatives.) Your nightly product build is an ideal time to automatically run your entire NUnit test suite, just to be absolutely certain that nothing was broken during the day.
The command line version can also save an XML log of test results. Figure B shows such a log open in Internet Explorer. This is useful when you’re calling NUnit as part of an automated build process; if anything fails, you can inspect the XML log to see where the failure lies.
Working with a net
I find that there are two immediate benefits to working with NUnit (or any other test-first development solution that allows me to easily build and run unit test suites). First, coding actually goes faster when you write the tests first. That seems paradoxical, but I think it’s a matter of mental focus. At any given time, I’m focused directly on whatever mini-milestone (some developers call these “inch-pebbles”) I need to meet to make the next test work. I’m no longer tempted to go charging around the program, implementing a dozen things at once as long as I’m in the code.
Even more important is the feeling of security that this style of development can bring to the programmer. For example, have you ever released an application and worried that perhaps something had broken at the last minute when you made hurried code changes? That’s much less likely to happen when you use NUnit. By testing each piece of the application as you write it, and making sure that all of the tests always turn out right, you can gain confidence in your own work. Ultimately, that translates to delivering better code—and happier clients. I think those benefits are well worth the effort of learning to write tests first.