Microsoft & .NET.NETFrictionless Testing with TestDriven.NET

Frictionless Testing with TestDriven.NET

Much has been written in the last several years about the benefits of
unit testing and test-driven development. The basic idea is that you should
write a test for every bit of functionality in your application, and exercise
these tests as you go along to make sure that you didn’t break anything. Proper
use of these techniques can lead to increased confidence and fewer errors in
your code. A number of tools, such as NUnit, MbUnit, and even (for
those of you brave enough to run the Visual Studio 2005 beta) Microsoft Team
System
now support unit testing for .NET development. But a new entrant
to the .NET ecosphere, TestDriven.NET,
makes the process easier than ever. In this article, I’ll show you how.

Writing and Running Your First Test

Other tools, such as NUnit, run as external applications. They test your code
outside of the IDE. TestDriven.NET, in contrast, hooks unit testing directly up
to Visual Studio .NET. It can also make use of any of the testing frameworks
I’ve already mentioned; it comes with a bundled copy of MbUnit, so that’s what
I’ll use here. To get started, download a copy of TestDriven.NET and install it.
The download is free, although you do need to register with the site to get it.
Then create a new Visual Basic .NET class library project using Visual Studio
.NET 2003.

The simple project I’ll develop will let callers determine some properties of
numbers. I’ve added two classes to the project: Number.vb, which will contain
the actual code for the class, and NumberTests.vb, which will contain test code.
To see the mechanics of TestDriven.NET, I’ll start with a simple implementation
of Number. The first thing this class needs to do is to be able to set the value
of the number and retrieve it. Here’s a skeleton that defines the proper
interface:


Public Class Number

    Public Sub New()

    End Sub

    Public Property Value() As Integer
        Get

        End Get
        Set(ByVal Value As Integer)

        End Set
    End Property

End Class

You’re probably looking at that code and thinking that there’s no way that it
could possibly work – and you’re right. But one of the tenets of test-driven
development is to write tests before you write code. That way you can be sure
you know why you’re writing each bit of code. So, let’s move on to write
a test.

Because I’m going to use MbUnit for the actual testing, I need to add
references to MbUnit’s two libraries: MbUnit.Core.dll and MbUnit.Framework.dll.
These libraries get installed to the MbUnit folder beneath the TestDriven.NET
install location. With the MbUnit objects available, it’s time to write the
start of the test class:


Imports System
Imports MbUnit.Core.Framework
Imports MbUnit.Framework

 _
Public Class NumberTests

     _
    Public Sub TestValue()
        ' Make sure we can store and retrieve a value
        Dim num As New Number
        num.Value = 5
        Assert.AreEqual(num.Value, 5)
    End Sub

End Class

MbUnit, like the other .NET unit-testing frameworks, uses attributes to carry
metadata. The TestFixtureAttribute attribute indicates that this
class contains tests. The Test attribute on the
TestValue method indicates that this method is a unit test. This
particular test instantiates a Number object, sets its value, and
then tests the value using the MbUnit Assert object. If the two
arguments to the AreEqual method are the same, the test is a
success; otherwise, it’s a failure.

Now it’s time to see how easy it is to run a test using TestDriven.NET.
Right-click in the test class and select Run Test(s). TestDriven.NET will
compile the project, find the appropriate tests, run them, and display the
results in the Visual Studio .NET output pane. In this case, the output ends
with the ominous warning:


0 succeeded, 1 failed, 0 skipped, took 0.00 seconds

Yes, the test failed (which is hardly surprising, because the Value property
is only partially implemented). Even better, MbUnit saves its output as an HTML
file, and TestDriven.NET obligingly provides a link to this file in the output
pane. Control-click the link to open the output report right in the IDE, as
shown in Figure 1. As you can no doubt guess, the unhappy red colors
indicate that not all is well here.

Making Things Work

Now that you know for sure that the code in Number.vb isn’t working, it’s
time to fix it. Here’s a revised version of that class:


Public Class Number

    Public Sub New()

    End Sub

    Private _value As Integer

    Public Property Value() As Integer
        Get
            Return _value
        End Get
        Set(ByVal Value As Integer)
            _value = Value
        End Set
    End Property

End Class

Now if you run the unit test, you’ll find that the output window reads:


1 succeeded, 0 failed, 0 skipped, took 0.00 seconds.

The MbUnit report turns a soothing, happy green with passed tests, too. With
this little bit of functionality working, you can go on to write more code. But
remember: write tests first, then write code. After a few iterations of this
procedure, you might end up with a Number class that looks like this:


Public Class Number

    Public Sub New()

    End Sub

    Private _value As Integer

    Public Property Value() As Integer
        Get
            Return _value
        End Get
        Set(ByVal Value As Integer)
            _value = Value
        End Set
    End Property

    Public ReadOnly Property IsEven() As Boolean
        ' Returns true if the number is even
        Get
            Return _value Mod 2 = 0
        End Get
    End Property

    Public ReadOnly Property IsOdd() As Boolean
        ' Returns true if the number is odd
        Get
            Return _value Mod 2 = 1
        End Get 
    End Property

    Public ReadOnly Property IsPositive() As Boolean
        ' Returns true if the number is positive
        Get
            Return _value > 0
        End Get
    End Property
End Class

And here’s the corresponding test class:


Imports System
Imports MbUnit.Core.Framework
Imports MbUnit.Framework

 _
Public Class NumberTests

     _
    Public Sub TestValue()
        ' Make sure we can store and retrieve a value
        Dim num As New Number
        num.Value = 5
        Assert.AreEqual(num.Value, 5)
    End Sub

     _
    Public Sub TestIsEven()
        ' Make sure even numbers are property identified
        Dim num As New Number
        num.Value = 5
        Assert.AreEqual(num.IsEven, False)
        num.Value = 8
        Assert.AreEqual(num.IsEven, True)
    End Sub

     _
    Public Sub TestIsOdd()
        ' Make sure odd numbers are property identified
        Dim num As New Number
        num.Value = 5
        Assert.AreEqual(num.IsOdd, True)
        num.Value = 8
        Assert.AreEqual(num.IsOdd, False)
    End Sub

     _
    Public Sub TestIsPositive()
        ' Make sure positive numbers are property identified
        Dim num As New Number
        num.Value = -1
        Assert.AreEqual(num.IsPositive, False)
        num.Value = 0
        Assert.AreEqual(num.IsPositive, False)
        num.Value = 1
        Assert.AreEqual(num.IsPositive, True)
    End Sub

End Class

You’ll note that the test class is actually longer than the code that it’s
testing. This isn’t unusual if you’re doing a thorough job of test-driven
development, because you’ll usually want to test a variety of inputs for each
part of your code. Figure 2 shows the MbUnit output for the classes in this
state, with every test passing.

Successful test report from MbUnit

But Wait, There’s More!

So far, everything I’ve shown you could be done with any of the major unit
testing frameworks, though TestDriven.NET makes it easier by keeping it all
under one roof. But TestDriven.NET goes further to provide you with some
advanced capabilities as well. For example, if a test fails, it adds an entry to
the Visual Studio .NET task list. Double-clicking this entry takes you straight
to the failing test. The “Run Test(s)” menu items is also integrated with
Solution Explorer, so you can choose to run all the tests in a particular
project or class easily.

TestDriven.NET also allows you to choose between in-proc and debugger testing
with a simple menu selection. Normally, when you run your tests they just run,
and you look to your actual code for problems. But what do you do when you
suspect that the test itself might be in error? With TestDriven.NET, you set a
breakpoint in the test and choose to run the tests in the debugger, as shown in
Figure 3. When the test assembly hits the breakpoint, you can use all the
regular debugging tools to figure out what might be wrong. This is possible in
other unit testing tools (by attaching the debugger to the external process) but
TestDriven.NET makes it a whole lot easier.

Choices for running tests

Two other features deserve mention as well. First, TestDriven.NET runs in its
own process, which means that it’s very unlikely to cause side effects with the
code that it’s testing. This also means that it can be stopped easily – in fact,
when you’re running tests, there’s a Visual Studio .NET menu item to abort the
rest of the test run. That’s very useful when some test is taking much longer
than expected. Second, you can run any method as an “ad-hoc test”. Put
the cursor inside it and choose to run tests, and you can see the output to the
output pane. There are some limits to this facility (notably, there’s no way to
set input parameters), but it’s a great way to experiment quickly with code.

About the Author

Mike Gunderloy is the author of over 20 books and numerous articles on
development topics, and the lead developer for Larkware. Check out his latest book, Coder to Developer from Sybex. When
he’s not writing code, Mike putters in the garden on his farm in eastern
Washington state.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories