AutoFixture

There are more ways than one to skin a cat, or if you are a late 19th century west country gentleman, there are more ways of killing a cat than choking it with cream.

And similarly there are more ways than one to unit test an application. Martin Fowler, who is much more intelligent and experienced than I, discusses this in his blog here. In particular he talks about 2 schools of thought with regard to collaborator isolation.  Classic-style testers are happy to test their units in collaboration with their dependencies, and will only isolate a unit from a dependency if need be.  Whereas Mockist-style testers use test doubles, and in particular mocks, to isolate their units from all dependencies. Jay Fields introduced the terminology, which I like very much, for these schools of thought as sociable testing versus solitary testing.

Like flavours of ice-cream, there’s no right or wrong. Martin classes himself as a classic-style tester, so it certainly can’t be wrong. However, if the community blogs and forums are a good indicator, I would say that the majority of testers are mockists. Or at least, the mockist-style is the current flavour of the month.

The aim of unit testing is to verify the behaviour of a unit, or SUT (System Under Test), ensuring that all relevant paths – happy, alternative, and error – are adequately covered. Mockists will typically isolate the SUT from it’s dependencies by creating test doubles using a mocking framework such as Moq, NSubstitute, or FakeItEasy.

Mocking frameworks greatly simplify unit testing by taking a lot of the leg work out of creating objects that simulate the behaviour of the dependencies. Mocking frameworks save a lot of lines of code, and additionally provide functionality to help verify the communication between the SUT and the dependency. But I’m not here to talk about mocking. I’m here to talk about how we can further simplify unit testing and save even more lines of code by using AutoFixture to help us construct inputs, or test fixtures, for our SUT.

AutoFixture is an open source library created by Mark Seemann. To quote the blurb on the AutoFixture GitHub repository –

“AutoFixture is an open source library for .NET designed to minimize the ‘Arrange’ phase of your unit tests in order to maximise maintainability.Its primary goal is to allow developers to focus on what is being tested rather than how to setup the test scenario, by making it easier to create object graphs containing test data.”

It takes the heavy lifting out of creating test fixtures, reducing the coding effort and lines of code required to create and maintain tests.

My SUT is FilmService, as shown below, and I want to test the happy path of the AddFilm method.

using Sandbox461.Models;
using System;

namespace Sandbox461.Domain
{
    public class FilmService
    {
        private Sandbox461DbContext db;

        public FilmService(Sandbox461DbContext db)
        {
            this.db = db;
        }

        public Film AddFilm(Film film)
        {
            if (film == null)
            {
                throw new ArgumentNullException();
            }

            db.Films.Add(film);
            db.SaveChanges();

            return film;
        }
    }
}

As mockists the first thing we do is create mocks for all classes that our SUT depends on. In this case, an instance of Sandbox461DbContext is injected into FilmService via the constructor. FilmService therefore depends on Sandbox461DbContext.

The happy path of the AddFilm method collaborates with Sandbox461DbContext in 2 ways. Firstly, the Add method of the Films property – which is of type DbSet<Film> – is called. And secondly, the SaveChanges method of Sandbox461DbContext is called. In order to verify that the SUT calls both these methods, we need to create mocks for both the Sandbox461DbContext class and the Films property, and we need to set up the mock for Sandbox461DbContext to return the mock for the Films property.

We then create an instance of FilmService and call the AddFilm method. However, the AddFilm method requires an instance of Film, and in this case we manually create and that instance and set the required properties to appropriate values. The AddFilm method is then called.

The test verifies that the Add method of the Films mock is called once – hence the instance of film is added to the Films set. It then verifies that the SaveChanges method of the Sandbox461DbContext mock is called once. And finally the test asserts that the AddFilm method returns the Index view.

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Data.Entity;
using Sandbox461.Models;
using Sandbox461.Domain;
using System.Collections.Generic;

namespace Sandbox461Tests.Domain
{
    [TestClass]
    public class FilmServerTests
    {
        [TestMethod]
        public void AddFilm_HappyPath()
        {
            // Arrange
            var mockFilmsSet = new Mock<DbSet<Film>>();
            var mockContext = new Mock<Sandbox461DbContext>();
            mockContext.Setup(m => m.Films).Returns(mockFilmsSet.Object);

            var sut= new FilmService(mockContext.Object);

            var film = new Film
            {
                FilmId = 0,
                Title = "Test Title",
                Actors = new List<FilmMaker>()
                {
                    new FilmMaker() { FilmMakerId = 1, FirstName = "Billy Bob", LastName = "Thornton" },
                    new FilmMaker() { FilmMakerId = 1, FirstName = "Martin", LastName = "Freeman" },
                    new FilmMaker() { FilmMakerId = 1, FirstName = "Ross", LastName = "Whitehead" }
                },
                Directors = new List<FilmMaker>()
                {
                    new FilmMaker() { FilmMakerId = 1, FirstName = "Randall", LastName = "Einhorn" },
                    new FilmMaker() { FilmMakerId = 1, FirstName = "Michael", LastName = "Uppendahl" },
                    new FilmMaker() { FilmMakerId = 1, FirstName = "Adam", LastName = "Bernstein" }
                }
            };

            // Act
            var result = sut.AddFilm(film);

            // Assert
            mockFilmsSet.Verify(m => m.Add(It.IsAny<Film>()), Times.Once());
            mockContext.Verify(m => m.SaveChanges(), Times.Once());

            Assert.IsInstanceOfType(result, typeof(Film));
        }
    }
}

The instance of Film takes quite a bit of effort, and quite a few line of code to set-up. And as we develop the application Film will most likely be extended to include addition properties, such as StoryLine, Genres, Certificate, and Budget, thus becoming quite complex, and requiring many line of code to manually create the instance.

You may be questioning why I created 3 instances of FilmMaker in each of the Actors and Directors collections, where they are not required for this test. The reason is that when I introduce AutoFixture to the test, then this is exactly what AutoFixture will generate. So I wanted a like-for-like comparison.

Another issue with manually creating our test fixtures is that every time we modify the classes, possibly adding required properties, then we have to modify our tests. It would be nice if we could avoid this overhead.

So how can we reduce the effort required to set-up and maintain our test fixtures? This is cue for AutoFixture to enter stage left..

I’ve refactored the test to use AutoFixture, by replacing the explicit creation of the Film object with the following 2 lines of code –

var fixture = new Fixture();
var film = fixture.Create<Film>();

The first line of code creates an instance of the Ploeh.AutoFixture.Fixture class. And the second line uses the Create method of the Fixture instance to generate a “test fixture” of type Film.

If we take a look at the object graph for film, as shown below, we can observe the following –

  • AutoFixture populates all publically set-able fields.
  • Any fields that have private setters are not populated.
  • Unless they are set via the constructor, is which case AutoFixture will pass appropriate values through the constructor.
  • AutoFixture uses Constrained Non-Determinism to populate the values. Basically, it uses algorithms to generate non-deterministic values that are likely to stay away from any of our tests boundary conditions. The reason Mark states for using non-deterministic values is that it enforces good discipline by making it impossible to hard-code expected values in our tests. The algorithms can however be customized if need be.
  • For strings, the algorithm generates a new GUID converted to a string.
  • Numbers are random, and for integers the algorithm generates random numbers within the range of [1, 255] for the first 255 numbers, and then [256, 32767] for the remaining.
  • DataTimes are random, constrained to the past 2 and next 2 years.
  • Chars are also random.
  • For collections, such as List<T>, AutoFixture will populate the collection with 3 entries, because 3 is many. Although the number of entries can be changed by setting the Fixture.RepeatCount property.

film

The revised test is as follows –

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Data.Entity;
using Sandbox461.Models;
using Sandbox461.Domain;
using Ploeh.AutoFixture;
using System.Collections.Generic;
using Ploeh.AutoFixture.AutoMoq;

namespace Sandbox461Tests.Domain
{
    [TestClass]
    public class FilmServerTests
    {
        [TestMethod]
        public void AddFilm_HappyPath_WithAutoFixture()
        {
            // Arrange
            var mockFilmsSet = new Mock<DbSet<Film>>();
            var mockContext = new Mock<Sandbox461DbContext>();
            mockContext.Setup(m => m.Films).Returns(mockFilmsSet.Object);

            var sut= new FilmService(mockContext.Object);

            var fixture = new Fixture();
            var film = fixture.Create<Film>();

            // Act
            var result = sut.AddFilm(film);

            // Assert
            mockFilmsSet.Verify(m => m.Add(It.IsAny<Film>()), Times.Once());
            mockContext.Verify(m => m.SaveChanges(), Times.Once());

            Assert.IsInstanceOfType(result, typeof(Film));
        }
    }
}

In summary, if you’re a mockist then you want to mock the dependencies, create test fixtures for the inputs, and then verify the behaviour and outputs for all paths in your SUT. A mocking framework will simplify the mocking of dependencies, whilst AutoFixture will simplify the creation of test fixtures. You will write less code, and your code will be durable as you will most likely not have to modify test code based on modification to the test fixture classes.

For more information on AutoFixture check out Mark Seemann’s blog. And consider buying him a cup of coffee. It’s a worthy read.

Also, check out the AutoFixture GitHub repository.

And install AutoFixture from Nuget.

 

 

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s