Monday, June 6, 2011

Fluent Assertions

Fluent assertions are a way of making unit test assertions more human readable. I've been using them for some time, and have come to really appreciate the difference they make in transforming the way a unit test is read. It's a pretty simple concept, best illustrated by an example.

A traditional (if there is such a thing) unit test might look something like this:

[TestClass]
public class MyServiceLocatorSecs
{
    [TestMethod]
    public void Resolve_ResolvingAnInterface_ReturnsType()
    {
        var sut = new MyServiceLocator(new UnityContainer());
        sut.RegisterType<IServiceA, ServiceA>();

        var results = sut.Resolve<IServiceA>();

        // basic mstest assertion
        Assert.IsInstanceOfType(results, typeof (ServiceA));
    }

    public interface IServiceA { }
    public class ServiceA : IServiceA { }
}


However, if we use the built-in nUnit-style asserts, we see the assertion reads a little easier:

[TestClass]
public class MyServiceLocatorSecs
{
    [TestMethod]
    public void Resolve_ResolvingAnInterface_ReturnsType()
    {
        var sut = new MyServiceLocator(new UnityContainer());
        sut.RegisterType<IServiceA, ServiceA>();

        var results = sut.Resolve<IServiceA>();

        // nUnit-style assertion
        Assert.That(results, Is.TypeOf<ServiceA>());
    }

    public interface IServiceA { }
    public class ServiceA : IServiceA { }
}

Finally, we can use some form of helper assembly to make the a truly fluent assertion:

[TestClass]
public class MyServiceLocatorSecs
{
    [TestMethod]
    public void Resolve_ResolvingAnInterface_ReturnsType()
    {
        var sut = new MyServiceLocator(new UnityContainer());
        sut.RegisterType<IServiceA, ServiceA>();

        var results = sut.Resolve<IServiceA>();

        // fluent assertions
        results.Should().BeOfType<ServiceA>();
    }

    public interface IServiceA { }
    public class ServiceA : IServiceA { }
}

I've found that I really like the way the fluent assertions read. There are a number of ways to include these in your projects. You can include a code file, or NuGet to import an assembly. Heck, you can even roll your own if you really wanted.

The end result are tests which read very clearly. When combining fluent assertions with context/specification unit test, you wind up with tests that very much describe the code:

[TestClass]
public class WhenResolvingAContract : GivenAServiceLocator
{
    private IServiceA results;

    protected override void Context()
    {
        base.Context();
        Container.Resolve<IUnityContainer>()
            .RegisterType<IServiceA, ServiceA>();
    }

    protected override void BecauseOf()
    {
        results = Sut.Resolve<IServiceA>();
    }

    [TestMethod]
    public void ShouldReturnAnInstanceOfTheRegisteredClass()
    {
        results.Should().BeOfType<ServiceA>();
    }
}