Thursday, 4 June 2009

NUnit 2.5 Dabblings

Version 2.5 of NUnit was released recently. As I often do when a new version of a tool is released, I looked to see what's new. This also often gives me the opportunity to take a peek at features that were there in the last version which I hadn't noticed or hadn't had a reason to make use of. Looking back at unit tests I've written to date I've not been that adventurous in my use of NUnit. Here are the assertions I've mostly used.

Assert.IsTrue
Assert.IsFalse
Assert.AreEqual

I have also occasionally used the alternative "fluent" syntax


but found that they don't offer much for simple asserts, e.g.,


Assert.That(x, Is.EqualTo(y))


vs


Assert.AreEqual(x, y)


The former doesn't offer anything over the latter and is more unwieldy to write. However, the fluent form comes into its own in contexts like this:


Assert.That(x, Is.EqualTo(y).Within(0.000001))


This could also have been written


Assert.AreEqual(x, y, 0.000001)


but in this case it is clear that the fluent form is more readable.


Parameterised Tests

These allow you to supply data to a test case via parameters. The MbUnit framework added them some time ago via their RowTest attribute and prior to NUnit 2.5 it was possible to get the same behaviour via an NUnit extension. Parameterised tests aid in reducing code duplication for tests that use the same algorithm but with differing inputs. Consider a simple test of an email address validation routine.


Example Using Test Attribute
[Test]
public void ValidEmailInUsername()
{
string email = "joe@abc.co.uk";
Assert.IsTrue(ValidationTool.IsValidEmail(email));
}

[Test]
public void ValidEmailWithPeriodInUsername()
{
string email = "joe.bloggs@abc.com";
Assert.IsTrue(ValidationTool.IsValidEmail(email));
}

[Test]
public void ValidEmailWithUnderscoreInUsername()
{
string email = "joe_bloggs@abc.com";
Assert.IsTrue(ValidationTool.IsValidEmail(email));
}

[Test]
public void ValidEmailWithHyphenInUsername()
{
string email = "joe-bloggs@abc.com";
Assert.IsTrue(ValidationTool.IsValidEmail(email));
}

Of course, there is no real logic in the test cases in this simple example but we can see how we can easily start to get nasty code duplication.  As the tests develop we can factor out this code into helper routines but we're still faced with staring at a bunch of tests that look structurally the same.


When we load these in NUnit we get:


image


Example Using TestCase Attribute - First Version
[TestCase(
"joe@abc.co.uk",
Description = "Valid email in username"
)]
[TestCase(
"joe.bloggs@abc.com",
Description = "Valid email with period in username"
)]
[TestCase(
"joe_bloggs@abc.com",
Description = "Valid email with underscore in username"
)]
[TestCase(
"joe-bloggs@abc.com",
Description = "Valid email with hyphen in username"
)]
public void ValidEmail(string email)
{
Assert.IsTrue(ValidationTool.IsValidEmail(email));
}

Loading this in the NUnit GUI produces the following:


image


However, there is a disadvantage in that you cannot easily tell what is being tested. In the GUI you can see different parameters being passed to each test but you don't get a description such as ValidEmailWithPeriodInUsername, ValidEmailWithUnderscoreInUsername, etc. This is described in a post at Vadim Kreynin's blog. In our case we just have a single parameter but clearly it would be even worse with multiple parameters.


However, we can improve on this scenario while still getting the benefit of the TestCase attribute. We can add the TestName attribute.


Example Using TestCase Attribute - Second Version
[TestCase(
"joe@abc.co.uk",
Description = "Valid email in username",
TestName = "ValidEmailInUserName"
)]
[TestCase(
"joe.bloggs@abc.com",
Description = "Valid email with period in username",
TestName = "ValidEmailWithPeriodInUsername"
)]
[TestCase(
"joe_bloggs@abc.com",
Description = "Valid email with underscore in username",
TestName = "ValidEmailWithUnderscoreInUsername"
)]
[TestCase(
"joe-bloggs@abc.com",
Description = "Valid email with hyphen in username",
TestName = "ValidEmailWithHyphenInUsername"
)]
public void ValidEmail(string email)
{
Assert.IsTrue(ValidationTool.IsValidEmail(email));
}

Loading this in the NUnit GUI produces the following:


image


We also get the valid email tests nicely grouped as sub-nodes of ValidEmail. So we get both the visual readability of the Test attribute and the elimination of code duplication of the TestCase attribute. Nice.