Unit Testing for Fun and Profit with NUnit 2.0

Fun?

Testing for fun? Are you kidding? I can’t tell you the number of developers who had this reaction the first time I introduce it. Once they started applying these techniques however, they began to see things my way. Of course it is not the testing per say that is fun, it is the results of the testing that makes programming fun. Once you start unit testing the quality of the code you produce goes up resulting in less bugs the QA department sends back to you. Since you are not busy fixing bugs, you get to work on other new and cool stuff.

 

Remember that chuck of code you wrote months (or years) ago, but were afraid to touch even thought it really needed it because you might break something? What if you had a whole series of unit tests that would tell you where you broke it? Would you feel more confident about making those needed changes, not to mention the elimination of the fear of shipping some obscure (or not so obscure) bug.

What is a Unit Test?

A unit test is a small piece of code that checks to see if a specific feature is functioning as expected, i.e.:

 

Assertion.AssertEquals("MySqrt failed", 1.7724531, MyMath.Sqrt(Math.PI), 0.001);

 

In this example we are testing a custom implementation of the Sqrt() routine by providing a message that is displayed if the test fails, the expected value, the actual value and an acceptable delta (to account for rounding etc.).

Why Should I Write Unit Tests?

You don’t have to –  unless you have to maintain code written by someone else, give code to others to maintain, want to make sure that bug you fixed doesn’t creep back in, want confidence that the new feature you added doesn’t break something else, etc, etc.

What Should I Test?

The simplistic answer is everything. However, everything does not have an equally likely chance of having a bug. So the more practical answer is “everything that could possibly break”.

 

Realize of course that the goal of testing is to break things, not show that they work.

 

You can use any of the normal methods for determining what you should test including requirements specifications, structured basis testing, data-flow testing and boundary analysis.

When Should I Write the Tests?

Ideally the tests should be written before the code is (see Test First Development below). Next best is just after the code has been written.

 

The other major time new tests are written is when QA or (heaven forbid) your customer finds an error. Then you need to immediately create a test or series of tests that expose the error before you fix it. Otherwise how do you know you really fixed it?

 

But you say “I have gobs of code that don’t have any tests. Where do I start?” The best place is the code you are changing now, write some tests, then make the changes and then write some more tests, and so on.

Unit Testing Frameworks

Now that you have lots of good tests how do you know they working correctly? One way is to write all your tests in a single routine and invoke it somehow. Another is to create test classes that contain groups of tests for different functional areas. But rather than reinvent the wheel make use of the xUnit unit testing frameworks listed at http://www.xprogramming.com/software.htm.

 

NUnit

NUnit is a unit testing framework for code targeting Microsoft .NET. NUnit was originally ported from jUnit, but has undergone some significant changes in version 2 to make it more .NET-like.

 

NUnit itself was written in C#, but is capable of testing any of the .NET languages through the magic of the CLR.

 

One assumption that NUnit has is that the NUnit-GUI and NUnit-console applications are used to test compiled code, rather than in the IDE, although there is an effort to integrate NUnit into the IDE.

 

NUnit can be downloaded from http://sourceforge.net/projects/nunit. NUnit is an open source project distributed under an OSI approved license.

 

NUnit 2.0 HowTo

Writing unit tests with NUnit is very simple,

  1. Create a test class with the TestFixture attribute.
  2. Add a reference to nunit.framework.
  3. Add “using Nunit.Framework; (or equivalent)
  4. Write methods with the Test attribute that test various things using the static methods of the Assert class. These methods should be public void with no parameters.

 

When you run the GUI  or console test runner all your tests (or a subset if specified) will be executed and the results displayed.

 

Here is a sample test class that tests a alternate implementation of the square root function:

using System;

using Nunit.Framework;

 

namespace MyTester

{

   [TestFixture]

   public class MyMathTest

   {

 

       [Test]

       public void Baseline()

       {

          Assertion.AssertEquals("Sqrt failed for 0", 0, MyMath.Sqrt(0), 0.001);

          Assertion.AssertEquals("Sqrt failed for 1", 1, MyMath.Sqrt(1), 0.001);

          Assertion.Assert("Sqrt failed for -1", Double.IsNaN(MyMath.Sqrt(-1)));

          Assertion.AssertEquals("Sqrt failed for PI", 1.7724531023414977791280875500565, MyMath.MySqrt(Math.PI), 0.001);

          Assertion.AssertEquals("Sqrt failed for 10,000", 100, MyMath.Sqrt(10000), 0.001);

       }

   }

}

 

As a convention test fixture classes are the name of the class being tested suffixed with “Test”. I.e. MyMathTest is the test fixture class that tests MyMath.

 

After loading the assembly into the NUnit GUI and running the tests we have the green bar of success.

 

 

If necessary there are SetUp and TearDown attributes for methods that get called once before the tests in each TestFixtue class are run (SetUp) and once after (TearDown). Both methods are public void with no parameters.

 

[SetUp]

public void SetUp()

{

      //code to load the test data from disk

}

 

[TearDown]

public void TearDown()

{

      testData.Clear();

      testData = null;

}

 

Additionally there are ExpectedException and Ignore attributes.

 

The ExpectedException attribute tells the framework to expect the named exception to be thrown, and if not the test fails.

 

i.e.

[Test]

[ExpectedException(typeof(OverflowException))]

public void Baseline()

 

The Ignore attribute tells the framework to ignore the attributed class or method when running tests.

 

i.e. to ignore a single test method

[Test]

[Ignore("deciding on NaN vs Exceptions")]

public void Baseline()

 

or to ignore all test methods in a class

 

[TestFixture]

[Ignore("Math library being re-written with foobar optimizations")]

public class MyMathTest

 

 

Notice that ignored tests are highlighted on the Tests Not Run tab so that they are not forgotten. The Ignore attribute is a convenience attribute so that you don’t have to comment out tests and then forget about them.

Test Driven Development

Test driven development (sometimes called test first development) is a style of coding characterized by writing tests first, then writing just enough code to make the test pass and then repeating.

 

The advantage to test driven development is that you write code (the unit test) that uses the feature before you actually implement it. This allows you to see how the feature will be called in client code, resulting in cleaner designs and APIs.

 

A second advantage is that if you only write code to satisfy the tests, and the tests are based on the desired features you are less likely to write code that won’t be used or is overly complicated.

 

One of the things that makes test driven development work is the library of other completed unit test. These tests allow you to refactor the code so that the new feature (or bug) being tested can be implemented even if it requires major changes to existing code. The existing test will let you know if you broke something. This style of coding is sometimes called “emergent design” since the design “emerges” from the code rather than being done up front as in traditional waterfall methodologies.

 

http://groups.yahoo.com/group/testdrivendevelopment/

Additional Resources

Test Infected: Programmers Love Writing Tests (JUnit.org)

Introducing Test-First Development (Sticky Minds)

Test Driven Development (Object Mentor)

jUnit Articles (junit.org)

Unit Tests (WikiWikiWeb)

 

Copyright © 2002 Wayne Allen, All Rights Reserved