Invitation Digital Tech Blog

Building Scalable & Responsive Architecture

By

An introduction to unit testing with AutoFixture

Maintaining a high level of unit test coverage shouldn’t feel like a struggle. However, when refactoring code, the process of updating unit tests can feel like an arduous task.

AutoFixture is a unit testing framework that has received a lot of praise in recent months. Its aim is to make the setup of our unit tests a whole lot easier.

The story so far…

As a tech department that strives for good quality code, we are always pushing to ensure that we have a high level of test coverage throughout our code base.

We aim to write code that is as testable as possible. In a class that is dependent on external services, commands, queries, etc, we inject these dependencies using dependency injection (DI). We create unit tests that have many lines of setup code to satisfy these dependencies. This can often be a time consuming process and in the end there are actually only a handful of lines of code for executing the method we are testing and then asserting the result is what we expect. As our dependencies evolve, all our tests need updating to reflect these changes. The more dependencies, the more brittle our tests become. Where possible, we split out our code to make it more succint and to ensure that classes have a single responsibility. However, the evolution of our code base usually results in a lot changes within the setup of our unit tests.

In our production code, we rely heavily on DI containers to make the job of injecting dependencies in to our classes far less of a headache. This code is hidden nicely away in a bootstrap folder and we only edit it from time to time when adding new dependencies that haven’t been set up before.

Within our unit test code, the story is quite different. Too often, we feel reluctant about making changes knowing that this will lead to lots of changes to tests that we don’t necessarily care about, only to satisfy the dependencies of the class under test.

AutoFixture to the rescue

Many developers in the .NET community will know of Mark Seemann. To remind yourself, he’s the author of the book, Dependency Injection in .NET. He’s also written two Pluralsight courses on unit testing and written the unit testing library known as AutoFixture. The main driver behind AutoFixture is its aim to allow you to write less testing code and to make the process a lot simpler and cleaner.

AutoFixture has seen a lot of movement in the .NET community and even more so in the last few months. With a focus on productivity, it even boasts about allowing you to write 30% less test code when you start using AutoFixture. I’m always keen to learn about new technologies and having never used AutoFixture before I was keen to know more about it.

Giving it a go

To make it a little easier I started by creating a small test project that contained a dummy service to simulate a typical process based around adding, updating and deleting users within our application.

public class UserService
{
    private readonly IUserByEmailQuery _userByEmailQuery;

    private readonly ICreateUserCommand _createUserCommand;

    public UserService(
        IUserByEmailQuery userByEmailQuery,
        ICreateUserCommand createUserCommand,
        IDeleteUserCommand deleteUserCommand)
    {
        _userByEmailQuery = userByEmailQuery;
        _createUserCommand = createUserCommand;
        _deleteUserCommand = deleteUserCommand;
    }

    public CreateUserResponse Post(CreateUser request)
    {
        var userResult = _userByEmailQuery.Execute(new UserByEmailParameters { Email = request.Email });

        if (userResult != null)
        {
            throw new HttpError(HttpStatusCode.BadRequest, "User already exists");
        }

        var createResult = _createUserCommand.Execute(
            new CreateUserParameters
            {
                DateOfBirth = request.DateOfBirth,
                Email = request.Email,
                FirstName = request.FirstName,
                LastName = request.LastName,
                Password = request.Password
            });

        return new CreateUserResponse
        {
        	UserId = createResult.UserId
        };
    }
}

As you can see, the UserService requires two dependencies, IUserByEmailQuery and ICreateUserCommand. Let’s try writing a simple unit test to prove that an exception is thrown when a user already exists.

[TestFixture]
public class UserServiceTests
{
    private IUserByEmailQuery _userByEmailQuery;

    private ICreateUserCommand _createUserCommand;

    [SetUp]
    public void Setup()
    {
        _userByEmailQuery = Substitute.For<IUserByEmailQuery>();
        _createUserCommand = Substitute.For<ICreateUserCommand>();
    }

    [Test]
    public void HttpErrorThrownWhenCreatingUserAlreadyExists()
    {
        // Arrange
        _userByEmailQuery
            .Execute(Arg.Any<UserByEmailParameters>())
            .Returns(new VouchercloudUser());

        var service = new UserService(_userByEmailQuery, _createUserCommand);

        var request = new CreateUser
        {
            DateOfBirth = new DateTime(1980, 1, 1),
            Email = "james@vouchercloud.com",
            FirstName = "James",
            LastName = "Harper",
            Password = "Password"
        };

        // Act, Assert
        service
            .Invoking(s => s.Post(request))
            .ShouldThrow<HttpError>().And.Message.Should().Be("User already exists");

        _createUserCommand
            .DidNotReceive()
            .Execute(Arg.Any<CreateUserParameters>());
    }
}

If you are unfamiliar with any of the syntax used above, I’m using NUnit as the base framework, NSubstitute for mocking and FluentAssertions for asserting any expectations.

Using AutoFixture

Let’s see how this code changes when we use AutoFixture.

[TestFixture]
public class UserServiceTestsAutoFixture
{
    private ICreateUserCommand _createUserCommand;

    private IUserByEmailQuery _userByEmailQuery;

    private IFixture _fixture;

    [SetUp]
    public void Setup()
    {
        _fixture = new Fixture();
        _fixture.Customize(new AutoNSubstituteCustomization());

        _userByEmailQuery = _fixture.Freeze<IUserByEmailQuery>();
        _createUserCommand = _fixture.Freeze<ICreateUserCommand>();
    }

    [Test]
    public void HttpErrorThrownWhenCreatingUserAlreadyExists()
    {
        // Arrange
        _userByEmailQuery
            .Execute(Arg.Any<UserByEmailParameters>())
            .Returns(new VouchercloudUser());

        var service = _fixture.Create<UserService>();

        var request = new CreateUser
        {
            DateOfBirth = new DateTime(1985, 12, 11),
            Email = "james@vouchercloud.com",
            FirstName = "James",
            LastName = "Harper",
            Password = "Password"
        };

        // Act, Assert
        service
            .Invoking(s => s.Post(request))
            .ShouldThrow<HttpError>().And.Message.Should().Be("User already exists");

        _createUserCommand
            .DidNotReceive()
            .Execute(Arg.Any<CreateUserParameters>());
    }
}

What’s all the fuss about? What’s changed? Well, not much really… The test itself is pretty much identical, except this time instead of creating an instance of UserService we let AutoFixture create the instance for us like this:

var service = _fixture.Create<UserService>();

Setup() is where bulk of the changes have been made. Firstly, we create our instance of Fixture and customise it to use NSubstitute. AutoFixture supports other mocking frameworks but in my example I have chosen to use NSubstitute. Next we call Freeze on our Fixture to say that whenever there is a requirement for an IUserByEmailQuery or an ICreateUserCommand then use the instance that we’ve created here.

We discussed previously about how AutoFixture can make refactoring our code easier. What if we wanted to update our UserService to include another dependency?

public class UserService
{
    private readonly ICreateUserCommand _createUserCommand;

    private readonly IUserByEmailQuery _userByEmailQuery;

    private readonly IDeleteUserCommand _deleteUserCommand;

    public UserService(
        IUserByEmailQuery userByEmailQuery,
        ICreateUserCommand createUserCommand,
        IDeleteUserCommand deleteUserCommand)
    {
        _userByEmailQuery = userByEmailQuery;
        _createUserCommand = createUserCommand;
        _deleteUserCommand = deleteUserCommand;
    }

    ...
    ...
}

As you can see, UserService now has a dependency on IDeleteUserCommand. What about the unit test that we wrote earlier? Surely we must update this also? Well, no! AutoFixture has done the hard work for us. Because we are using _fixture.Create<UserService>(); to create our instance and telling it to use AutoNSubstituteCustomization, we don’t need to do anything to tests we have already written. It’s like depencency injection for unit tests! Pretty cool eh?!

We’ve only brushed the surface of what AutoFixture can do, but I hope you can appreciate that it’s pretty impressive. AutoFixture is a great framework and real productivity changer. Anything that allows us to focus more on writing quality code is a definite bonus.

If you’d like to download my sample project and have a play with AutoFixture you can do this by accessing my public GitHub repository.