CodeProject, Uncategorized

AutoMocking Container

At work I am lucky enough to work with a few bright chaps (sadly some of them are a lot youger than me, which is making me question my existence 😦 , but on the other hand it is always good to learn new things).

One of those new things for me happened the other day, where this dude at work showed me MSpec for TDD/BDD, and I have to say it was pretty awesome. The thing that I liked the most was the “AutoMocking” feature

So what is this “AutoMocking” that I am talking about, and why is it cool? Well quite simply it eliviates the brittle relationship with code and tests in their infancy, and allows tests to create constructor injection parameters automatically

I should point out that this pattern really suits new code bases, that may still be a bit in flux, it is not really that much use for well established code bases that have been around and settled for a while

It is also worth noting that this pattern can be used with code that doesn’t use any IOC/dependency injection at all, it is purely a testing concern

So what is it all about?

Well say you have this class

public class Autobooker
{
    private readonly ILogger logger;
    private readonly IRateProvider rateProvider;
    private readonly IBooker booker;

    public Autobooker(ILogger logger, IRateProvider rateProvider, IBooker booker)
    {
        this.logger = logger;
        this.rateProvider = rateProvider;
        this.booker = booker;
    }


    public void BookDeal(string ccy1, string ccy2, decimal amount)
    {
        logger.Log(string.Format("Booked deal for {0}/{1}", ccy1, ccy2));
        decimal rate = rateProvider.GetRate(string.Join("", ccy1, ccy2));
        booker.Book(ccy1, ccy2, rate, amount);

    }
}

Which you may write a test case for something like this (I am using NUnit and Moq, but you may prefer you own tools to these)

[TestFixture]
public class TestCases
{
    private IWindsorContainer container;

    [SetUp]
    public void SetUp()
    {
        container = new WindsorContainer();
        container.Install(new BookerInstaller());
    }

    [TestCase("EUR", "GBP", 1500)]
    [TestCase("GBP", "EUR", 2200)]
    public void TypicalTestCase(string ccy1, string ccy2, decimal amount)
    {
        //setup
        string ccyPair = string.Join("", ccy1, ccy2);
        var rate = 1;
        var loggerMock = new Mock<ILogger>();
        var rateProviderMock = new Mock<IRateProvider>();
        var bookerMock = new Mock<IBooker>();

        rateProviderMock
            .Setup(x => x.GetRate(ccyPair)).Returns(1);

        var autobooker = new Autobooker(loggerMock.Object, rateProviderMock.Object, bookerMock.Object);
        autobooker.BookDeal(ccy1, ccy2, amount);

        //assert
        loggerMock
            .Verify(x => x.Log(string.Format("Booked deal for {0}/{1}", ccy1, ccy2)),
                Times.Exactly(1));

        rateProviderMock
            .Verify(x => x.GetRate(string.Join("", ccy1, ccy2)), Times.Exactly(rate));

        bookerMock
            .Verify(x => x.Book(ccy1, ccy2, rate, amount), Times.Exactly(1));
    }
}

The problem with this code is that it is extremely coupled to the design. What would happen if the Autobooker class needed to take a new constructor dependency say something like this

public class Autobooker
{
    private readonly ILogger logger;
    private readonly IRateProvider rateProvider;
    private readonly IBooker booker;
    private readonly IPricer pricer;

    public Autobooker(ILogger logger, IRateProvider rateProvider, IBooker booker, IPricer pricer)
    {
        this.logger = logger;
        this.rateProvider = rateProvider;
        this.booker = booker;
        this.pricer = pricer;
    }
}

This should immediately ring alarm bells that your existing test cases will now break, but what can we do about it? This is where “AutoMocking” can help. Let us take a look at this shall we.

So the first step is to decide on a nice IOC container that you think is fit for the job. For me this is Castle Windsor. So that is that decision made, so what do we need to do now that we have made that decision? Well all we really need to do is get it to automatically create our Mocks for us. SO what does that look like? Well for me it looks like this

public class AutoMoqServiceResolver : ISubDependencyResolver
{
    private IKernel kernel;

    public AutoMoqServiceResolver(IKernel kernel)
    {
        this.kernel = kernel;
    }

    public bool CanResolve(
        CreationContext context,
        ISubDependencyResolver contextHandlerResolver,
        ComponentModel model,
        DependencyModel dependency)
    {
        return dependency.TargetType.IsInterface;
    }

    public object Resolve(
        CreationContext context,
        ISubDependencyResolver contextHandlerResolver,
        ComponentModel model,
        DependencyModel dependency)
    {
        var mock = typeof(Mock<>).MakeGenericType(dependency.TargetType);
        return ((Mock)kernel.Resolve(mock)).Object;
    }
}

The next step is to create some sort of IOC registration for the system under test (SOT), which for me means the Autobooker type. So we do that as follows

public class BookerInstaller : IWindsorInstaller
{
    public void Install(
        IWindsorContainer container,
        IConfigurationStore store)
    {
        container.Kernel.Resolver.AddSubResolver(new AutoMoqServiceResolver(container.Kernel));
        container.Register(Component.For(typeof(Mock<>)));

        container.Register(Classes
            .FromAssemblyContaining<Booker.Autobooker>()
            .Pick()
            .WithServiceSelf()
            .LifestyleTransient());
    }
}

With that all in place I can now write non brittle code, that will get the object I want to test from the IOC container, where all its dependencies will try to be satified by the automocking IOC container. So my test case now looks like this

[TestCase("EUR", "GBP", 1500)]
[TestCase("GBP", "EUR", 2200)]
public void TestBooking(string ccy1, string ccy2, decimal amount)
{
    var autobooker = container.Resolve<Autobooker>();
    string ccyPair = string.Join("", ccy1, ccy2);
    var rate = 1;
            
    //arrange
    container.Resolve<Mock<IRateProvider>>()
        .Setup(x => x.GetRate(ccyPair)).Returns(1);

    autobooker.BookDeal(ccy1,ccy2,amount);

    //assert
    container.Resolve<Mock<ILogger>>()
        .Verify(x => x.Log(string.Format("Booked deal for {0}/{1}", ccy1, ccy2)),
            Times.Exactly(1));

    container.Resolve<Mock<IRateProvider>>()
        .Verify(x => x.GetRate(string.Join("", ccy1, ccy2)), Times.Exactly(rate));
            
    container.Resolve<Mock<IBooker>>()
        .Verify(x => x.Book(ccy1,ccy2,rate,amount), Times.Exactly(1));

}

See how I no longer need to declare the mocks, I just define their behavior, which I think is cool

7 thoughts on “AutoMocking Container

  1. Although I think AutoMocking can be useful at times, I’m not convinced by the example you gave, since the problem can easily be solved by extracting the creation of the SUT to an factory method as follows:

    private Autobooker CreateBooker(
    ILogger logger = null,
    IRateProvider rateProvider = null,
    IBooker booker = null,
    IPricer pricer = null)
    {
    return new Autobooker(
    logger ?? new Mock().Object,
    rateProver ?? new Mock().Object,
    booker ?? new Mock().Object,
    pricer ?? new Mock().Object);
    };

    Using this factory method, only this method has to be changed when the constructor of the Autobooker is extended, in order to get the code compiling and all the tests working again. Not the 200 unit tests for the Autobooker class. A test can simply use the method as follows:

    var loggerMock = new Mock();

    // We only have to pass in the objects we’re interested in.
    var booker = CreateBooker(logger: loggerMock.Object);

    booker.DoSomething();

    loggerMock.Verify(someCondition);

    1. Steven

      I think your comment about using a factory is a fair one. I still think automocking is good at times as it saves you the manual mock creation step too

  2. Neat idea thanks.
    I recently experienced the pain of adding a new parameter and breaking a bunch of tests and I knew as I sat there copying & pasting my way through the fix there must be a better way 🙂
    I actually ended up with a mixture of both the solutions above – a factory method that returns a container to which I also added some of the classes used/reused as parameters as named registrations.
    Made everything a lot more concise & readable. Cheers.

    1. Alex

      Glad you think this is useful. I too have used the factory approach in the past. I do however like the fact it saves me an extra line of code for each Mock. But yeah both factory and this work just fine.

Leave a comment