Test-Driven Development in .NET

The NUnit Testing Framework

NUnit 2.0 is a radical departure from its ancestors. Those systems provided base classes from which you derived your test classes. There simply was no other way to do it. Unfortunately, they also imposed certain restrictions on the development of test code because many languages (like Java and C#) only allow single inheritance. This meant that refactoring test code was difficult without introducing complicated inheritance hierarchies. .NET introduced a new concept to programming that solves this problem: attributes. Attributes allow you to add metadata to your code. They typically don't affect the running code itself, but instead provide extra information about the code you write. Attributes are most often used to document your code, but they can also be used to provide information about a .NET assembly to a program that has never seen the assembly before. This is exactly how NUnit 2.0 works. The Test Runner application scans your compiled code looking for attributes that tell it which classes and methods are tests. It then uses reflection to execute those methods. You don't have to derive your test classes from a common base class. You just have to use the right attributes. NUNit provides a variety of attributes that you use when creating unit tests. They are used to define test fixtures, test methods, setup and teardown methods. There are also attributes for indicating expected exceptions or to cause a test to be skipped.

TestFixture Attribute

The TestFixture attribute is used to indicate that a class contains test methods. When you attach this attribute to a class in your project, the Test Runner application will scan it for test methods. The following code illustrates the usage of this attribute. (All of the code in this article is in C#, but NUnit will work with any .NET language, including VB.NET. See the NUnit documentation for additional information.)

namespace UnitTestingExamples
{
using System;
using NUnit.Framework;

[TestFixture]
public class SomeTests
{
}
}

The only restrictions on classes that use the TestFixture attribute are that they must have a public default constructor (or no constructor which is the same thing).

Test Attribute

The Test attribute is used to indicate that a method within a test fixture should be run by the Test Runner application. The method must be public, return void, and take no parameters or it will not be shown in the Test Runner GUI and will not be run when the Test Fixture is run. The following code illustrates the use of this attribute:

namespace UnitTestingExamples
{
using System;
using NUnit.Framework;

[TestFixture]
public class SomeTests
{
[Test]
public void TestOne()
{
// Do something...
}
}
}

SetUp & Teardown Attributes

Sometimes when you are putting together Unit Tests, you have to do a number of things, before or after each test. You could create a private method and call it from each and every test method, or you could just use the Setup and Teardown attributes. These attributes indicate that a method should be executed before ( SetUp ) or after ( Teardown ) every test method in the Test Fixture. The most common use for these attributes is when you need to create dependent objects (e.g., database connections, etc.). This example shows the usage of these attributes:

namespace UnitTestingExamples
{
using System;
using NUnit.Framework;

[TestFixture]
public class SomeTests
{
private int _someValue;

[SetUp]
public void Setup()
{
_someValue = 5;
}

[TearDown]
public void TearDown()
{
_someValue = 0;
}

[Test]
public void TestOne()
{
// Do something...
}
}
}

Expected Exception Attribute

It is also not uncommon to have a situation where you actually want to ensure that an exception occurs. You could, of course, create a big try..catch statement to set a boolean, but that is a bit hack-ish. Instead, you should use the ExpectedException attribute, as shown in the following example:

namespace UnitTestingExamples
{
using System;
using NUnit.Framework;

[TestFixture]
public class SomeTests
{
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void TestOne()
{
// Do something that throws an
// InvalidOperationException
}
}
}

When this code runs, the test will pass only if an exception of type InvalidOperationException is thrown. You can stack these attributes up if you need to expect more than one kinds of exception, but you probably should it when possible. A test should test only one thing. Also, be aware that this attribute is not aware of inheritance. In other words, if in the example above the code had thrown an exception that derived from InvalidOperationException , the test would have failed. You must be very explicit when you use this attribute.

Ignore Attribute

You probably won't use this attribute very often, but when you need it, you'll be glad it's there. If you need to indicate that a test should not be run, use the Ignore attribute as follows:

namespace UnitTestingExamples
{
using System;
using NUnit.Framework;

[TestFixture]
public class SomeTests
{
[Test]
[Ignore("We're skipping this one for now.")]
public void TestOne()
{
// Do something...
}
}
}

If you feel the need to temporarily comment out a test, use this instead. It lets you keep the test in your arsenal and it will continually remind you in the test runner output.

The NUnit Assertion Class

In addition to the attributes used to identify the tests in your code, NUnit also provides you a very important class you need to know about. The Assertion class provides a variety of static methods you can use in your test methods to actually test that what has happened is what you wanted to happen. The following sample shows what I mean:

namespace UnitTestingExamples
{
using System;
using NUnit.Framework;

[TestFixture]
public class SomeTests
{
[Test]
public void TestOne()
{
int i = 4;
Assertion.AssertEquals( 4, i );
}
}
}

(I know that isn't the most relevant bit of code, but it shows what I mean.)

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Theory is when you know something, but it doesn't work. Practice is when something works, but you don't know why. Programmers combine theory and practice: Nothing works and they don't know why.”