In this post I’m going to show you how you can write your unit test assertions to read more fluently and allow you to be more productive.
The Plain Jane Unit Test Assertion
[Test]
public void The_Plain_Jane_Assertion()
{
const string firstString = "Hello World";
const string secondString = "Hello World";
const int smallNumber = 1;
const int bigNumber = 500;
// Are Equal
Assert.AreEqual(firstString, secondString);
// Greater Than
Assert.Greater(bigNumber,smallNumber);
// Less Than
Assert.Less(smallNumber,bigNumber);
}
This is what most developers will start off using. No problem with the equals assertion it easy enough to read and use.
However when using the greater or less than assertions, it’s not clear if the first argument should be the lower or the higher value as shown in the screen shot below.
This distraction can easily stop your flow.
The More Fluent NUnit Test Assertion
[Test]
public void The_More_Fluent_Nunit_Assertion()
{
const string firstString = "Hello World";
const string secondString = "Hello World";
const int smallNumber = 1;
const int bigNumber = 500;
// Are Equal
Assert.That(firstString, Is.EqualTo(secondString));
// Greater Than
Assert.That(bigNumber, Is.GreaterThan(smallNumber));
// Less Than
Assert.That(smallNumber, Is.LessThan(bigNumber));
}
The unit test assertions read a lot more fluently, we no longer have to be concerned about the order of arguments.
If you don’t want to use another third party library, this is for you.
The Super Fluent NBehave Spec Assertion
[Test]
public void The_Super_Fluent_NBehave_Spec_Assertion()
{
const string firstString = "Hello World";
const string secondString = "Hello World";
const int smallNumber = 1;
const int bigNumber = 500;
// Are Equal
firstString.ShouldEqual(secondString);
// Greater Than
bigNumber.ShouldBeGreaterThan(smallNumber);
// Less Than
smallNumber.ShouldBeLessThan(bigNumber);
}
This example uses the NBehave Spec Framework which you can download from the NBehave site. In addition to being very easy to understand, the use of extension methods means there is a lot less noise.
Some developers will be concerned by losing the word ‘Assert’ in the line that does the assertion. If you arrange your tests using the Arrange, Act, Assert pattern this should not be a problem. See the example below.
[Test]
public void The_NBehave_Spec_Assertion_Using_Arrange_Act_Assert()
{
// Arrange
const int inputValue = 1;
// Act
var result = inputValue + 1;
// Assert
result.ShouldBeGreaterThan(inputValue);
}
In the last couple of months I’ve seen a new convention of how developers are naming their unit test classes.
Traditionally the naming convention most developers use is to append the word ‘Tests’ to the class name as shown below.
public class UserValidation
{
}
[TestFixture]
public class UserValidationTests
{
}
There is absolutely nothing wrong with it, but this can lead to problems because a test class can become unwieldy and difficult to maintain either because it’s too long, testing a lot of different concerns or both.
In some cases it would make more sense to take different approach and adopt an alternative naming convention. Sticking with the example above instead of having tests for validating a user in one class, a new class could be created for each concern. So instead of
using System;
using NUnit.Framework;
using NBehave.Spec.NUnit;
namespace MyApplication.Validation
{
[TestFixture]
public class UserValidationTests
{
[Test]
public void Should_Return_False_When_Username_Length_Is_To_Short()
{
// Arrange
UserValidation userValidation = new UserValidation();
// Act
bool result = userValidation.IsUsernameValid(string.Empty);
// Assert
result.ShouldEqual(false);
}
[Test]
public void Should_Return_False_When_Username_Length_Is_To_Long() { }
[Test]
public void Should_Return_False_When_Username_Contains_Invalid_Characters() { }
[Test]
public void Should_Return_True_When_Username_Is_Valid() { }
[Test]
public void Should_Return_False_When_Password_Length_Is_To_Short() { }
[Test]
public void Should_Return_False_When_Password_Length_Is_To_Long() { }
[Test]
public void Should_Return_False_When_Password_Is_Just_Numbers()
{
// Arrange
UserValidation userValidation = new UserValidation();
// Act
bool result = userValidation.IsPasswordValid("123456");
// Assert
result.ShouldEqual(false);
}
[Test]
public void Should_Return_False_When_Password_Is_Just_Alphabetical_Characters() { }
[Test]
public void Should_Return_False_When_Password_Contains_Invalid_Characters() { }
[Test]
public void Should_Return_True_When_Password_Is_Valid() { }
}
}
we create two test classes
using System;
using NUnit.Framework;
using NBehave.Spec.NUnit;
namespace MyApplication.Validation
{
[TestFixture]
public class When_Validating_A_Users_Username
{
[Test]
public void Should_Return_False_When_Username_Length_Is_To_Short()
{
// Arrange
UserValidation userValidation = new UserValidation();
// Act
bool result = userValidation.IsUsernameValid(string.Empty);
// Assert
result.ShouldEqual(false);
}
[Test]
public void Should_Return_False_When_Username_Length_Is_To_Long() { }
[Test]
public void Should_Return_False_When_Username_Contains_Invalid_Characters() { }
[Test]
public void Should_Return_True_When_Username_Is_Valid() { }
}
[TestFixture]
public class When_Validating_A_Users_Password
{
[Test]
public void Should_Return_False_When_Password_Length_Is_To_Short() { }
[Test]
public void Should_Return_False_When_Password_Length_Is_To_Long() { }
[Test]
public void Should_Return_False_When_Password_Is_Just_Numbers()
{
// Arrange
UserValidation userValidation = new UserValidation();
// Act
bool result = userValidation.IsPasswordValid("123456");
// Assert
result.ShouldEqual(false);
}
[Test]
public void Should_Return_False_When_Password_Is_Just_Alphabetical_Characters() { }
[Test]
public void Should_Return_False_When_Password_Contains_Invalid_Characters() { }
[Test]
public void Should_Return_True_When_Password_Is_Valid() { }
}
}
I think this behavior driven development type naming convention will help focus on the problem you are solving and nothing more. This is very useful when doing test driven development.
When I first saw tests written like this my first thought was how am I going to find the all the tests for User Validation because they are no longer in a single class? If this is a problem for you think about using namespaces, folders or test attributes to organise your tests. Don’t forget if you use tools like ReSharper help you find where a class is being used very easily.
Remember this is about renaming your tests classes and not your methods. So even if you follow the MethodName_StateUnderTest_ExpectedBehavior pattern for test methods this could still work.