Friday, November 30, 2012

Test Pattern, Given

Test Pattern
The Idea was to simplify validation of dependent functionality and is really useful if your already following the Exception over Assert pattern.
The concept is to use the similar comparisons to Assert to reduce the amount of code written in validation. I didn't want to use Assert for this because any failed assertion will result in a failed test.

Below I have two tests that do the same thing, change a users name in a system.  If either test cannot login as the user the test will error (not fail), and if either test cannot change the users name the test will fail. In the first test we check the return value of the LoginAs() method and if its not true we throw a new exception. Int he second test we Given the same way we would use Assert and it validates the return value of  the LoginAs() method and it raises an exception if the condition doesn't pass. It's also quicker to write and easier to read.
Code:
[Test]
public void ChangeUserName_Exception()
{
    if(!LoginAs("Rick"))
        throw new Exception("Login failed as user");
    Assert.That(ChangeUserName("Rick Casady"));
}
 
[Test]
public void ChangeUserName_Given()
{
    Given.That(LoginAs("Rick"));
    Assert.That(ChangeUserName("Rick Casady"));
}

Here is a basic version of the Given class, it's good to have other comparison functions found in the Asset class like (NotNull(), AreEqual(), AreNotEqual(), ...)
Code:
public class Given
{
    static public void That(bool condition)
    {
        Given.That(condition, Is.True, null, null);
    }

    static public void That(object actual, IResolveConstraint expression, string message, params object[] args)
    {
        Constraint constraint = expression.Resolve();

        if (!constraint.Matches(actual))
        {
            MessageWriter writer = new TextMessageWriter(message, args);
            constraint.WriteMessageTo(writer);
            throw new Exception(writer.ToString());
        }
    }
}

Constraint and IResolveConstraint can be found in NUnit.Framework.Constraints

No comments: