Sep 11 2010

The Right Way to Do Automocking with Ninject

Category: Moq | TestingMatt @ 13:07

While StructureMap is definitely my IoC container of choice, it’s not the only game in town.  At my new job, our existing project uses Ninject.   One of my first tasks at this new job was integrating my SpecsFor “framework” into the existing projects.  The original version of SpecsFor worked with StructureMap and leveraged its auto-mocking container.  While Ninject has an auto-mocking extension, ninject.moq, it is sorely lacking when compared to StructureMap’s offering.  In this post, I’ll show you how to modify it into a useful automocking container, and I’ll also show you how easily SpecsFor can be adapted to Ninject using this working container.

Fixing Ninject.Moq

Ninject’s auto-mocking extension project can be found on github.  Yes, you have to build it from source if you want to use it.  However, don’t bother: the existing approach to auto-mocking is flawed and doesn’t work.  Here’s a very simple test case to illustrate the problem:

[Test]
public void Kernel_returns_same_mock_instance_for_all_requests_for_that_type()
{
    var kernel = new MockingKernel();

    //This assert fails.
    Assert.AreSame(kernel.Get<IDummyService>(), kernel.Get<IDummyService>());
}

That’s right, the container returns a new mock every time, which makes it difficult to setup expectations to be injected into a class.  It also can’t create the class-under-test directly without jumping through some additional hoops.  You have to bind the class to itself, like so:

[Test]
public void Kernel_can_create_class_under_test()
{
    var kernel = new MockingKernel();
    //Why should I have to do this??
    kernel.Bind<DummyClass>().ToSelf();

    Assert.IsNotNull(kernel.Get<DummyClass>());
}

Fortunately, these problems are easy to fix, and in fact Derick Bailey has already fixed them in his port of ninject.moq to Rhino Mocks.  You can check out his code on github.

First, we need to eliminate the self-binding requirement for the class under test.  We can do that by adding a little extra logic to the MockingKernel’s HandleMissingBindingMethod:

protected override bool HandleMissingBinding(Type service)
{
    bool selfBindable = TypeIsSelfBindable(service);

    if (selfBindable)
    {
        Bind(service).ToSelf().InSingletonScope();
    }
    ...
}

If a type is self-bindable, we’re binding it to itself with singleton scope.  This means we’ll get the same instance of the class under test from our mocking kernel every time we request it until we call the kernel’s Reset method.

So, that eliminates the need for self-binding our class under test, but we still have the issue of the container returning brand new mocks every time we request one.  That’s because the container is using instance scope instead of singleton scope for the mocks.  Again, we can fix this by modifying the HanldeMissingBinding method, which should now look like this in its entirety:

protected override bool HandleMissingBinding(Type service)
{
    bool selfBindable = TypeIsSelfBindable(service);

    if (selfBindable)
    {
        Bind(service).ToSelf().InSingletonScope();
    }
    else
    {
        var binding = new Binding(service)
                          {
                              ProviderCallback = MockProvider.GetCreationCallback(),
                              ScopeCallback = ctx => Singleton,
                              IsImplicit = true
                          };

        AddBinding(binding);
    }


    return true;
}

With this change in place, both our test classes now pass!  You can check out this code on my ninject.moq fork on github.  I’ve submitted a pull-request, so hopefully the fixes will eventually be merged back into the main ninject.moq extension.

Using Ninject.Moq with SpecsFor<T>

With a working auto-mocking container in hand, all that remained was modifying SpecsFor to work with ninject.moq instead of StructureMap’s auto-mocking container.  I just swapped to the MockingKernel and explicitly created the class under test (StructureMap’s auto-mocking container creates the class under test for you automatically):

public abstract class SpecsFor<T> where T : class
{
    protected MockingKernel Mocker;

    protected T SUT { get; set; }

    /// <summary>
    /// Gets the mock for the specified type from the underlying container. 
    /// </summary>
    /// <typeparam name="TType"></typeparam>
    /// <returns></returns>
    protected Mock<TType> GetMockFor<TType>() where TType : class
    {
        return Mock.Get(Mocker.Get<TType>());
    }

    [TestInitialize]
    public virtual void SetupEachSpec()
    {
        InitializeClassUnderTest();

        Given();

        When();
    }

    protected virtual void InitializeClassUnderTest()
    {
        Mocker = new MockingKernel();

        ConfigureKernel(Mocker);

        SUT = Mocker.Get<T>();
    }

    protected virtual void ConfigureKernel(IKernel kernal)
    {
    }

    [TestCleanup]
    public virtual void TearDown()
    {
        Mocker.Reset();
        AfterEachSpec();
    }

    protected virtual void Given()
    {

    }

    protected virtual void AfterEachSpec()
    {

    }

    protected abstract void When();
}

Just to prove that it works, here’s the specs for SpecsFor (sadly using MS Test instead of NUnit.  Did you know there are people who actually use MS Test?  I didn’t either!) :)

public class SpecsForSpecs
{
    #region Dummy Classes

    public interface IDummyService
    {
        string GetSomeValue();
    }

    public class DummyClass
    {
        public IDummyService Service { get; set; }

        public DummyClass(IDummyService service)
        {
            Service = service;
        }
    }

    #endregion

    [TestClass]
    public class When_using_specs_for : given.the_default_state
    {
        protected override void When()
        {
            //Nothing to do
        }

        [TestMethod]
        public void Then_it_correctly_sets_up_the_class_under_test()
        {
            SUT.ShouldNotBeNull();
            SUT.Service.ShouldNotBeNull();
        }

        [TestMethod]
        public void Then_it_should_create_a_mock_service_for_the_dependency()
        {
            Mock.Get(SUT.Service).ShouldNotBeNull();
        }

        [TestMethod]
        public void Then_GetMock_should_always_return_the_same_mock()
        {
            GetMockFor<IDummyService>().ShouldBeSameAs(GetMockFor<IDummyService>());
        }
    }

    [TestClass]
    public class When_setting_up_expectations_on_a_mock : given.the_default_state
    {
        protected override void When()
        {
            GetMockFor<IDummyService>()
                .Setup(s => s.GetSomeValue()).Returns("I am a mocked result.");
        }
        
        [TestMethod]
        public void Then_the_expectations_are_called_when_the_mock_is_used()
        {
            SUT.Service.GetSomeValue().ShouldEqual("I am a mocked result.");
        }
    }

    public static class given
    {
        public abstract class the_default_state : SpecsFor<DummyClass>
        {
            
        }
    }
}

And there you have it!  A working Ninject auto-mocking container, and a version of SpecsFor that takes advantage of it.

On a related topic, I’m considering spinning SpecsFor into a lightweight library that can be easily reused.  Is there any interest in that?

Tags:

blog comments powered by Disqus