Microsoft’s submission of the C# language and
the CLR (Common Language Runtime) as industry standards has led to
many open source projects based on the .NET platform. One such
notable project is NUnit, a unit-testing framework for all .NET
languages. I’ll demonstrate the simplicity of using NUnit to
effectively unit test your code.
Unit testing
Even though you may love developing code, you
may not find thoroughly testing the code very appealing. It ranks
right behind documentation. However, it’s much more cost-effective
for a developer to catch and fix problems during code development
than waiting for someone in QA to find the problem, which would
require the developer to revisit the code. This is the idea behind
unit testing.
Unit testing involves testing the public
interfaces of your application classes. That is, you test the
classes to verify they perform as expected. This encompasses
verifying that results are returned as expected, as well as proper
exception handling. NUnit is an excellent tool to aid with these
tasks. NUnit is a flexible testing framework for the .NET platform.
It provides an easy-to-use interface for running your test.
NUnit is freely available from the NUnit.org Web site for
both the Windows and Mono environments. (I’m using a Windows XP
machine for this article, but the examples have been tested with
Mono on SUSE 9.2 Linux distribution.) Let’s take a closer look at
the NUnit framework in action.
Working with NUnit
The NUnit architecture is straightforward. You
can be up and running with sample tests in minutes as opposed to
hours. Once you install it on a system, the framework is located in
the nunit.framework.dll file. This file should be made available to
your project to develop NUnit-based code. A reference may be added
via Visual Studio .NET, or you can place it in the bin directory of
your application. Next, you must add a reference to the NUnit
namespace to your code (or utilize the complete path to NUnit
classes). The following C# snippet demonstrates this:
using NUnit.Framework;
Here’s the VB.NET equivalent:
Imports NUnit.Framework
Now you can use the classes in the
NUnit.Framework namespace. But let’s examine the many NUnit
attributes before diving into code.
NUnit attributes
The NUnit framework utilizes custom attributes
to specify NUnit elements utilized in code. These attributes are
placed before the appropriate code elements. Here is a list of
these attributes:
-
ExpectedException includes
an exception that the method can return. -
Setup is an initialization
section of code executed before the actual unit tests are run. -
TearDown is a section of
code run after all the tests have run. -
Test is an individual unit
test. This is placed before a method to indicate it is a test. -
Ignore may be placed before
a unit test to disable it. NUnit ignores any test with this
attribute. -
TestFixture is a collection
of unit tests. This is placed before the class declaration to
specify the class can contain tests.
NUnit executes Setup first, then individual
tests (it executes in the order it’s listed in code if it isn’t
specified otherwise like running manually), and the TearDown
section runs last. NUnit doesn’t execute any code marked Ignore. In
addition, NUnit provides numerous assertions to be used for unit
testing (you may also develop custom methods), which are available
via these methods of the Assert class:
-
AreEqual asserts that two
objects are equal. Two objects are considered equal if both are
null or if they have the same value. -
AreSame asserts that two
objects refer to the same object. -
IsFalse asserts that a
condition is false. -
IsNull asserts that an
object is null. -
IsNotNull asserts that an
object is not null. -
IsTrue asserts that a
condition is true.
You use these assertions to unit test your
code. The best way to demonstrate it is via a code sample. The
following C# class will be tested using NUnit:
using System;
using NUnit.Framework;
namespace UnitTestingSampleClass {
public class ExampleClass {
public string ConvertToUpper(string val)
{
return val.ToUpper();
}
public string ConvertToLower(string val) {
return val.ToLower();
} } }
This is a very simple class that provides two
methods. The first method converts a string to uppercase, and the
second method does the reverse by converting to lowercase. You have
your class, but you still need to develop a corresponding NUnit
class to test it. The NUnit class follows:
using System;
using NUnit.Framework;
using UnitTestingSampleClass;
namespace UnitTestingSample {
[TestFixture]
public class TestClass {
private string test1 = null;
private string test2 = null;
ExampleClass testObj = null;
[SetUp]
public void init() {
testObj = new ExampleClass();
test1 = “techrepublic.com”;
test2 = TECHREPUBLIC.COM”;
}
[Test]
public void TestA() {
Assert.AreEqual(testObj.ConvertToLower(test2),
test2.ToLower());
}
[Test]
public void TestB() {
Assert.AreEqual(testObj.ConvertToUpper(test1),
test1.ToUpper());
}
[Test]
[ExpectedException(typeof(NullReferenceException))]
public void NullTest() {
Assert.AreEqual(testObj.ConvertToLower(null),””);
}
[Test]
[Ignore(“Not Used”)]
public void TestC() { }
[Test]
public void TestD() {
Assert.AreEqual(testObj.ConvertToLower(test1),
test1.ToUpper());
}
[TearDown]
public void End() {
test1 = null;
test2 = null;
testObj = null;
} } }
This class contains five tests:
- TestA: The
ConvertToLower method is tested by passing in a string value and
comparing to the result of the String class’s ToLower method. True
(test passed) is returned by Assert.AreEqual if they’re equal;
otherwise, false is returned, indicating the test failed. - TestB: The
ConvertToUpper method is tested by passing in a string value and
comparing to the result of the String class’s ToUpper method. True
(test passed) is returned by Assert.AreEqual if they are equal;
otherwise, false is returned, indicating the test failed. - NullTest:
Passing a null value to the ConvertToLower method is tested to test
whether the appropriate Exception is thrown. The Assert.IsEqual
test will pass (true) if both are equal or the specific exception
is thrown (NullPointerException); otherwise, false is returned to
fail the test. - TestC: This
test is not coded, so it’s tagged to be ignored. - TestD: This
test fails since it compares a call to the ToLower method with a
string converted to uppercase. This provides an example of what to
expect with a failed test.
In addition, the init method runs before the
tests (Setup) and the end (TearDown) method is called for cleanup
once the tests have completed. NUnit includes a graphical interface
for running the tests. You could test your sample with the
following command line to call the interface and run the tests:
nunit-gui “c:\UnitTestingSample.exe” /run
The assembly was located in the root of the C
drive on my system, so the path may be different on your system.
You should specify the complete path to the assembly in the first
parameter passed to nunit-gui. If you’d rather stick to the command
line, you can use the command-line version with the following
line:
nunit-console “c:\UnitTestingSample.exe”
This produced the following output on my
system:
NUnit version 2.2.0
Copyright (C) 2002-2003 James W. Newkirk, Michael C. Two, Alexei
A. Vorontsov,
Charlie Poole..
Copyright (C) 2000-2003 Philip Craig.
All Rights Reserved.
OS Version: Microsoft Windows NT 5.1.2600.0 .NET
Version: 1.1.4322.573
….N.F
Tests run: 4, Failures: 1, Not run: 1, Time: 0.1093876
Failures:
1) TestUnitTestingConsole.TestClass.TestD :
String lengths are both 11.
Strings differ at index 0.
expected:<“techrepublic.com”>
but was:<“TECHREPUBLIC.COM”>
———–^
at TestUnitTestingConsole.TestClass.TestD() in
c:\TestClass.cs:line 55
Tests not run:
1) TestUnitTestingConsole.TestClass.TestC : Not Used
Each approach provides numerous command-line
options that you may view by using the /help command-line switch.
The equivalent VB.NET code follows with the class to be tested
listed first:
Public Class VBNetTestClass
Public Function ConvertToUpper(ByVal val As String) As
String
Return val.ToUpper()
End Function
Public Function ConvertToLower(ByVal val As String) As
String
Return val.ToLower()
End Function
End Class
This is the actual NUnit class for testing:
Imports NUnit.Framework
Imports UnitTestClassVBNet
<TestFixture()> Public Class Class1
Private test1 As String
Private test2 As String
Private testObj As VBNetTestClass
<SetUp()> Public Sub Init()
testObj = New VBNetTestClass
test1 = “techrepublic.com”
test2 = “TECHREPUBLIC.COM”
End Sub
<Test()> Public Sub TestA()
Assert.AreEqual(testObj.ConvertToLower(test2),
test2.ToLower())
End Sub
<Test()> Public Sub TestB()
Assert.AreEqual(testObj.ConvertToUpper(test1),
test1.ToUpper())
End Sub
<Test(), Ignore(“sample ignore”)> Public Sub TestC()
End Sub
<Test()> Public Sub TestD()
Assert.AreEqual(testObj.ConvertToLower(test1),
test1.ToUpper())
End Sub
<Test(), ExpectedException(GetType(NullReferenceException))>
Public Sub NullTest()
Assert.AreEqual(testObj.ConvertToLower(Nothing), “”)
End Sub
<TearDown()> Public Sub CleanUp()
test1 = Nothing
test2 = Nothing
testObj = Nothing
End Sub
End Class
The sooner you catch runtime errors the
better
Unit testing allows you to catch runtime errors
much sooner than if you had to wait for a QA tester. In addition,
it allows you to fix errors while the code is fresh in your memory.
The process of creating tests before the actual code is gaining
acceptance as well, but that’s a topic for another time.
TechRepublic’s free .NET newsletter, delivered each Wednesday, contains useful tips and coding examples on topics such as Web services, ASP.NET, ADO.NET, and Visual Studio .NET. Automatically sign up today!