Test-driven development with NUnit

A Demonstration

To show just how easy this is, let's make a pretend project .

Our first task in the project is to create a class named Calculator that contains a method that adds two numbers. Now, given what I've just explained, the first task would be to first have one test that fails. So let's create a new Console Application project. This project will be our testing project. It's not the real project that we have to do. This project will contain the code that tests our production code.

In our Main() method let's add a few lines of code that determine whether out code (which does not exist yet) runs ok.

The first thing we want to do is test that we can create a new instance of our calculator class:

[STAThread]
static void Main(string[] args)
{
      Calculator calc = new Calculator();
}

Now, of course this code won't compile, but what we have just done is created a test that relies on the fact that a class named Calculator exists. So our next step is now to make the test pass . To do that we'll finally create our “real” project and add a class named calculator to it. Once we add a reference to that project into out test project, we'll see that the code compiles and runs just fine. We just made the test pass .

Our next goal? Add an “AddNumbers()” method that takes two numbers and returns the sum of those numbers. Again, what's the first thing we do?  That's right. We create a test that fails. So let's add code to test this to our previous test code:

[STAThread]
static void Main(string[] args)
{
      //test creation
      Calculator calc = new Calculator();

      //test addition
      int retVal = calc.Add(22,20);
      if(42!= retVal)
      {
            throw new Exception("calc.Add(2,2) did not return a value of 4");
      }
}

That was simple. Again, this code won't compile. We have to create the code that does this addition operation. Once we've added a simple method for Add() we can now re-run the test and make sure that it works. Let's make this a little more interesting though. What happens when we send in a null value in one of the parameters? Suppose that the design requires us to throw a CalcException when a null value is sent to this Add() method.  If want to add this feature, we first need to test for it:

//test exception
try
{
      int retVal = calc.Add(null,22);
}
catch(CalcException ce)
{
      //test passed
}
catch(Exception e)
{
      throw new Exception("calc.Add(null,22) did not throw a calcException");
}

Now the code won't compile again, We don't have a CalcException class defined. Once we define it, we can run the test, but it will fail again, since we'll get a standard exception and not a calcException from the AddMethod. So we'll change our code to throw that exception…. And so on…

As you can see this process is pretty easy. What I've done at every step is define a goal, and than make sure I pass it. What we have achieved at the end of this session is a piece of code that is thoroughly tested. Not only that, we get several added bonuses:

  • Anyone who looks at our tests can understand what out intention and purpose is of each method
  • We can make changes to our code and know “what we broke” with the click of a mouse and fix it just as fast
  • If our testing covered all of our code like this, we could find bugs in our programs at build time that would have taken a very long time at the customer's site

But some things are definitely missing from our current solution:

  • No automation. If I wanted to run a build and get the results of the tests that were performed I'd have a long day coming up with a solution that traces the messages and output them.
  • No re-use. I'd have to re-write any output handling from scratch every time I want to test a project
  • No decoupling. Code that runs a test must be totally decoupled from other code that runs a test. I always want my testing code to run within a given context, a known set of known values for parameters and so on. I don't want other tests messing up my state when they change stuff. There's no framework that allows me to have a separate state for each testing code without significant work every time.

You might also like...

Comments

About the author

Roy Osherove Israel

Roy Osherove has spent the past 6+ years developing data driven applications for various companies in Israel. He's acquired several MCP titles, written a number of articles on various .NET topic...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“We better hurry up and start coding, there are going to be a lot of bugs to fix.”