Testing Visual Basic .NET with NUnit
NUnit is a testing framework for all .NET languages. The basic idea is that you create a class library that calls into your code, sending test values and evaluating responses automatically. The big payoff is that NUnit can run these tests automatically and be integrated as part of your build, test, and deployment lifecycle.
Let me take a moment or two to describe how you can integrate NUnit into your software development lifecycle, and in so doing, offer good arguments for integrating NUnit into your development lifecycle.
Downloading and Installing NUnit
LNUnit V2.0 is available as an open source product from www.nunit.org. It is a framework implemented entirely in .NET for .NET languages. You can download the binaries and source for free and modify them—if you need to—as long as you adhere to the rules proscribed by nunit.org.
When you download NUnit you can select all of the default configuration options, which will place NUnit in the C:\Program Files\NUnit V2.0\ folder. For our purposes we will assume that NUnit is in the default location; however, you can install it anywhere.
After you have installed NUnit on your workstation you will see an NUnit shortcut on your desktop. This shortcut is for the GUI version. NUnit also ships a console version. We will use the GUI version here, but the console version might be ideal for an automated build, test, deploy cycle.
Building a Test Fixture
The basic process for defining a test is to create a class library, add a reference to the code you want to test to that library, define a class in the test class library, and adorn that class with the TestFixtureAttribute. TestFixtureAttribute is defined in the nunit.framework.dll assembly, so you'll need to add a reference to this assembly (found in C:\Program Files\NUnit V2.0\bin folder) and add an Imports statement to the .cs file containing your class library. After these steps NUnit will recognize your class library as one that will contain tests. The next step is to define those tests.
NUnit offers many tools for testing, but you can get started with a class adorned with the TestFixtureAttribute and just one method in that class adorned with the TestAttribute. Listing 1 offers a very simple class for the purposes of testing, and listing 2 shows you how easy it is to begin using NUnit. (Note: As a general practice you do not need to write your own sort. Any System.Array can be sorted by calling the Array.Sort shared method, which performs a quick sort. In addition, you can define an IComparer that will permit you to define custom comparisons for complex types. The sort method in listing 1 is simply there to demonstrate NUnit.)
Listing 1: Some code we want to test.
Public Class TestMePlease
Public Shared Sub Swap(ByVal Values() As Integer, _
ByVal I As Integer, ByVal J As Integer)
Dim Temp As Integer = Values(I)
Values(I) = Values(J)
Values(J) = Temp
End Sub
Public Shared Sub Sort(ByVal Values() As Integer)
Dim I, J As Integer
For I = Values.GetLowerBound(0) To Values.GetUpperBound(0) - 1
For J = I To Values.GetUpperBound(0)
If (Values(I) > Values(J)) Then
Swap(Values, I, J)
End If
Next
Next
End Sub
Public Shared Sub Dump(ByVal Values() As Integer)
Dim E As IEnumerator = Values.GetEnumerator
While (E.MoveNext)
Console.WriteLine(CType(E.Current, Integer))
End While
Console.WriteLine("press enter")
Console.ReadLine()
End Sub
End Class
Listing 1 contains a Bubble sort that plays the role of code we want to test. Here is listing 2 showing a simple test for the sort method.
Listing 2: An NUnit TestFixture that will test our code for us.
Imports NUnit.Framework
Imports LibraryToTest
<TestFixture()> _
Public Class Test
<SetUp()> _
Public Sub Init()
' Initialization code here
End Sub
<TearDown()> _
Public Sub Deinit()
' De-initialization code here
End Sub
<Test()> _
Public Sub TestSortPass()
Dim I() As Integer = {5, 4, 3, 2, 1}
TestMePlease.Sort(I)
Assertion.AssertEquals("Sort passed", 2, I(1))
End Sub
End Class
Listing 2 contains a TestFixture. The class library containing the class in listing 2 is loaded into NUnit. NUnit uses Reflection to find classes adorned with the TestFixtureAttribute, create instances of that class, and execute methods adorned with special attributes. For example, at the start of each test methods with SetUpAttribute are run, followed by a single TestAttribute method, and finished with the method (if any) marked with the TearDownAttribute.
In our example in listing 2 Init, TestSortPass, and Deinit are run in that order. I don't have any initialization code; if you need to create an object for a specific test then the method marked with the SetUpAttribute is a good place to do it. Let's take a look at how we define tests now.
Defining Tests
Test methods—that is, methods that will be called directly by NUnit—are marked with the TestAttribute. Test methods are subroutines that have no parameters. Reflection can invoke methods with parameters and return types, but we are not testing the test method, we are testing the code inside the method. Thus, everything you need to perform a single test should occur inside the test method and the initialization method.
My test method is named TestSortPass. Adding a Pass or Fail suffix is a convention I follow. In TestSortPass I created an array of integers to pass to my sort method. I invoked the Sort method on the code that I am testing and then checked to see if an arbitrary value was in the correct position. The statement containing Assertion.AssertEquals is the code that comes from the NUnit framework.
There are eight overloaded versions of AssertEquals alone. The arguments in the overloaded version I used are a message, the result, and the test value. The statement is understood to mean that I(1) = 2, or that the second position in the array should have the second lowest value.
When I load the class library containing the TestFixture into NUnit I will see a list of tests. You can click on a single test or the fixture and click Run to run those tests. Tests with a green circle succeeded and tests with a red circle failed (see figure 1).
Figure 1: The NUnit graphical user interface showing the test results for the TestSortPass test.
Advanced Teechniques
Even if all NUnit offered was a simple pass or fail test it would be worth using; however, the NUnit framework is quite extensive. You can use an IgnoreAttribute to mark tests that aren't quite ready. These tests won't run and will be so noted in NUnit with a yellow circle adjacent to the test. You can add an ExpectedExceptionAttribute and the type object of an exception to indicate that a test method should receive an exception and the type of the exception, and much more.
With a bit of cleverness you can devise useful tests for very complex applications. On a project I am working on we have even defined tests for ASP.NET. Exploring the source code, the online documentation, and experimentation will help you invent great ways to test your applications.
Understanding the Test Cycle with NUnit
NUnit makes a copy of your class library containing the tests. It then loads the copy into its own application domain. The benefit here is that you can change your code and recompile and NUnit will automatically detect that the test assembly or dependent assemblies have changed, reloading them without necessitating an NUnit restart. This is a nice feature that permits you to seamlessly edit, compile, test, and modify code without stopping, starting, and reloading test assemblies.
In addition to loading the test assembly into its own AppDomain, NUnit looks for a file named testassembly.dll.config. This allows you to associate a configuration file with your test assemblies. For example, if you have special configuration information—a TraceSwitch, for example—you can copy that information into a .config file that will be read and used by NUnit too. Normally only Web.config and application .config files are read by .NET assemblies, but because NUnit supports a .config file you will not have to modify code that is dependent on a .config file for NUnit testing.
Summary
NUnit is an open source framework for testing applications written into .NET. Because the cost is zero there should be little or no objection to incorporating NUnit into your software development cycle. If used effectively you'll discover that NUnit greatly facilitates unit testing and automated regression testing. Once a test is created as a class library and test fixtures and tests are defined you'll never have to write that test again. Simply run the test and you will instantly know what works, what was broken, and what just isn't working yet.
No comments:
Post a Comment