- Using the System.Diagnostics.Trace Class
- Using NUnit To Debug .NET Code
- Summary
Using NUnit To Debug .NET Code
What does NUnit have to do with TraceListeners? That's a good question. Let me start by telling you what NUnit is and what it does.
NUnit is an open source test application suite from http://www.nunit.org. It's very well designed, and uses natural features in .NET, permitting you and me to do a bang-up job with testing .NET code. The basic idea is that you download NUnit from the web site, create a new class library with some references to the code you want to test, and add a few attributes and some test-case methods. NUnit then dynamically loads and runs classes tagged with these specific attributes and uses a three-color coded system to tell you the outcome of the tests:
Red: The test failed.
Green: The test passed.
Yellow: The test was ignored. (You mark tests that you haven't finished writing with an IgnoreAttribute for yellow tests.)
Trust meNUnit is so easy to use that nonprogrammers can learn how to write test cases with it, and everyone can easily discern which tests passed and which failed.
Financially, I'm a non-interested party in NUnit; therefore, I can tell you without reservation that if you're using .NET you should incorporate NUnit into your software development process.
Tapping into Trace Statements with NUnit
Perhaps you just started religiously using trace statements in your code. Well, since we're on the topic of religion, why not start religiously writing test cases? I'm not advocating losing the extremely valuable data logged with trace statements. You can have both. By combining trace statements, TraceListeners, and NUnit, it's possible (and highly desirable) to create test cases that verbosely tell you what your code is doing. This is an invaluable strategy for robust software development. And, because these two tools.NET and NUnitmake it easy to do, it takes the guesswork out of writing and testing code.
Creating a Sample Application
Let's demonstrate. The sample application can be anything. Because we don't have unlimited time and space here, however, I've created a class library in C# that contains one method to test. It's worth noting that you can test code that's used with Remoting, COM+, and ASP.NET just as easily. (I know; I've done it.)
The simple class library is shown in Listing 1 without elaboration.
Listing 1 A Sample Class in a Class Library
using System; using System.Diagnostics; namespace Sample { public class PleaseTestMe { public string Name { get { Trace.WriteLine("PleaseTestMe.get_Name called"); return "PleaseTestMe"; } } } }
Testing with NUnit
Assuming that you've downloaded and installed the latest version of NUnit (I'm using NUnit 2.1), the next step is to add a class library containing the test cases. Granted, the sample class is easy, but you can use just about any code, and the tests can be simple or complex.
To create and run the tests, add the code for the test class library (see Listing 2) to the second class library and follow these steps:
In the new class library, add a reference to the nunit.framework.dll assembly. (If you installed NUnit, this assembly should be listed in the Add Reference dialog box's .NET tab.)
Add a reference to the class library to be tested. (This is our sample class from Listing 1.)
Add a using statementImports for Visual Basic .NETto the class module in the test library, and write the test code (see Listing 2).
Add a using statement for the Sample class's namespace.
Start an instance of NUnit.
Load the .dll assembly containing the tests.
Click the Run button.
Listing 2 An NUnit Test To Test Our Sample Class
using System; using NUnit.Framework; using Sample; namespace Tests { [TestFixture] public class Test { [Test] public void PleaseTestMeTest() { PleaseTestMe obj = new PleaseTestMe(); Assertion.AssertEquals("PleaseTestMe", obj.Name); } } }
Here's a synopsis of the test code:
The NUnit.Framework namespace simplifies access to NUnit capabilities.
The Sample namespace simplifies access to the code we want to test.
The TestFixtureAttribute ([TestFixture]) is used by NUnit to figure out which class or classes to load dynamicallythat is, which classes contain testsin order to run those tests. This is done with Reflection, an ultra-cool and powerful runtime type engine in .NET.
Finally, test methodsmethods that take no arguments and return nothingare tagged with the TestAttribute (dropping the Attribute suffix is a convention).
NOTE
The reason for the method signature is that NUnit designers had no way of knowing in advance what arguments to pass to or receive from your test methods.
Now we're ready to write some code. As long as that code runs without an exception, or passes the Assertion tests we write, the test passes. In the example, we're simply asserting that the object's name matches a predetermined value. This is representative of any test.
TIP
These tests can be very complex; for example, I've written multithreaded and asynchronous tests against COM+ objects and SQL Server databases easily.
Eavesdropping with a TraceListener
The final step is to eavesdrop on our trace messages in our test code. For this step we'll need to implement a TraceListener, add an instance of that listener to the Trace.Listeners collection, and tell the overridden methods to send the trace information to NUnit's Console.Out tab. This isn't as hard as it might seem. Listing 3 shows the revised code, with the new stuff in bold.
Listing 3 An NUnit Test with a Custom TraceListener
using System; using NUnit.Framework; using Sample; using System.Diagnostics; namespace Tests { public class MyListener : TraceListener { public override void Write(string message) { Console.Write(message); } public override void WriteLine(string message) { Console.WriteLine(message); } } [TestFixture] public class Test { public static MyListener listener = new MyListener(); [SetUp()] public void Init() { Trace.Listeners.Add(listener); } [TearDown()] public void Deinit() { Trace.Listeners.Remove(listener); } [Test] public void PleaseTestMeTest() { PleaseTestMe obj = new PleaseTestMe(); Assertion.AssertEquals("PleaseTestMe", obj.Name); } } }
The new using statement adds easy access to the System.Diagnostics namespace. The new class, MyListener, implements a custom TraceListener by defining the minimum-required methods, Write and WriteLine. These methods simply forward output to NUnit's Console (see Figure 1). Finally, we create a static instance of the new TraceListener, adding it to the Trace.Listeners collection when a test is initialized and removing it when the test has finished. Methods marked with the SetUpAttribute and TearDownAttribute denote test initialization and finalization methods, respectively. When we run the test, we should see the output as depicted in the figure.
Figure 1 The trace statement as it appears when forwarded to NUnit by the Trace class.
A Final Word on Strategy
Personally, I don't want to write the TraceListener custom class every time I create a test, and professionally I can't afford to do so. The solution is to create a new .NET project template that contains all the stubbed-out references and code for the listening NUnit test solution. .NET supports extending the available project templates with an existing wizard vsWizard.dll and a simple text editor. Unfortunately, we don't have time to do that here, but you can learn how to create project and element templates for .NET in my book Visual Basic .NET Power Coding (Addison-Wesley, 2003, ISBN 0672324075).