MSpec and Auto Mocking

February 23rd, 2010

In my previous post, I explained how to get started with Machine.Specifications (or MSpec for short) and showed you how the syntax for context/specifications looks like when using this BDD framework. For this post, I want to show you how to use an auto mocking container (we’ll be using the one provided by StructureMap off course).

We’ll use the same example as the one used in the previous post, but now we’ll deal with the message handler that makes a particular customer preferred.

//
// Subject under test
//
public class MakeCustomerPreferredMessageHandler
{
    private readonly ICustomerRepository<ICanMakeCustomerPreferred> _repository;

    public MakeCustomerPreferredMessageHandler(
        ICustomerRepository<ICanMakeCustomerPreferred> repository)
    {
        _repository = repository;
    }

    public void Handle(MakeCustomerPreferredMessage message)
    {
        var customer = _repository.Get(message.CustomerId);
        if(null == customer)
            throw new InvalidOperationException(
                "No customer for specified identifier");
        
        customer.MakePreferred();
        _repository.Save(customer);
    }
}

 

The Customer class implements a ‘role interface’ called ICanMakeCustomerPreferred. We retrieve a customer from the repository and make it preferred. We throw an exception in case the customer cannot be found in the data store.

Here are the context/specifications for this easy example:

[Subject("Making a customer preferred")]
public class when_making_a_customer_preferred
    : context_specification<MakeCustomerPreferredMessageHandler>
{
    Establish context = () =>
    {
        _message = new MakeCustomerPreferredMessage { CustomerId = 6412 };

        Repository.Stub(repository => repository.Get(_message.CustomerId))
            .Return(Customer);
    };

    Because of = () => 
        SUT.Handle(_message);
    
    It should_mark_the_customer_as_preferred = () =>
        Customer.AssertWasCalled(customer => customer.MakePreferred());

    It should_save_the_customer_in_the_repository = () =>
        Repository.AssertWasCalled(repository => repository.Save(Customer));

    private static ICanMakeCustomerPreferred Customer
    {
        get { return Dependency<ICanMakeCustomerPreferred>(); }
    }

    private static ICustomerRepository<ICanMakeCustomerPreferred> Repository
    {
        get { return Dependency<ICustomerRepository<ICanMakeCustomerPreferred>>(); }
    }

    private static MakeCustomerPreferredMessage _message;
}

[Subject("Making a customer preferred")]
public class when_making_an_unexisting_customer_preferred
    : context_specification<MakeCustomerPreferredMessageHandler>
{
    Establish context = () =>   
    {
        _message = new MakeCustomerPreferredMessage() { CustomerId = 61544 };

        Dependency<ICustomerRepository<ICanMakeCustomerPreferred>>()
            .Stub(repository => repository.Get(_message.CustomerId))
            .Return(null);
    };

    Because of = () =>
        _resultingException = Catch.Exception(() => SUT.Handle(_message));

    It should_result_in_an_error = () =>
        _resultingException.ShouldBeOfType<InvalidOperationException>();

    private static MakeCustomerPreferredMessage _message;
    private static Exception _resultingException;
}

 

I want to point out that all fields and properties are made static. This is needed so that the anonymous methods can access them. I’m also using a base class for these specifications which I’ll show next. This base class uses an auto mocking container for providing the requested mocks and stubs through the Dependency and Stub methods.

public abstract class context_specification<TSubjectUnderTest>
    where TSubjectUnderTest : class
{
    private static IAutoMockingContainer<TSubjectUnderTest> _autoMockingContainer;
    protected static TSubjectUnderTest SUT { get; set; }

    Establish context = () =>
    {
        _autoMockingContainer = new StructureMapAMC<TSubjectUnderTest>();
        SUT = _autoMockingContainer.Create();     
    };
    
    Cleanup stuff = () => 
    {
        SUT = null;
        _autoMockingContainer = null;
    };

    protected static TDependency Dependency<TDependency>()
        where TDependency : class
    {
        return _autoMockingContainer.GetMock<TDependency>();
    }

    protected static TStub Stub<TStub>()
        where TStub : class
    {
        return _autoMockingContainer.GetStub<TStub>();
    }
}

public interface IAutoMockingContainer<TSubject>
    where TSubject : class
{
    TSubject Create();
    TMock GetMock<TMock>() where TMock : class;
    TStub GetStub<TStub>() where TStub : class;
}

public class StructureMapAMC<TSubject> 
    : IAutoMockingContainer<TSubject>
    where TSubject : class
{
    private readonly RhinoAutoMocker<TSubject> _rhinoAutoMocker;

    public StructureMapAMC()
    {
        _rhinoAutoMocker = 
            new RhinoAutoMocker<TSubject>(MockMode.AAA);
    }

    public TSubject Create()
    {
        return _rhinoAutoMocker.ClassUnderTest;
    }

    public TMock GetMock<TMock>() 
        where TMock : class
    {
        return GetDependency<TMock>();
    }

    public TStub GetStub<TStub>() 
        where TStub : class
    {
        return GetDependency<TStub>();
    }

    private TDependency GetDependency<TDependency>() 
        where TDependency : class
    {
        return _rhinoAutoMocker.Get<TDependency>();
    }
}

 

Notice that I’m using the Establish and Cleanup delegates in the context_specification base class. This doesn’t prevent that these can be used again in derived context/specifications. MSpec ensures that the anonymous methods are called in the right order. This means that the Establish method of the base class is called before the Establish method of the derived context/specifications.    

Absolutely no rocket science here, but I figured it might come in handy when you need it. For the next post I’ll try to demonstrate how to deal with reusable behaviors.

  • http://elegantcode.com Richard Cirerol

    Jan, I can’t imagine doing tdd/bdd without MSpec and Automocking. I wrapped up the automocker as well. Glad to see I wasn’t alone in my thinking. Looking forward to the rest of your MSpec posts.

  • http://elegantcode.com Jan Van Ryswyck

    Didn’t know there was already a post on this blog. Will give it a thorough read.

  • http://elegantcode.com Richard Cirerol

    No problem – not harshing you in any way. Our code benefits from alternate perspectives and the continuing conversation.

    On a separate note, I wanted to forewarn you that the automocker’s ClassUnderTest object is immutable once created; therefore, its dependencies are immutable as well. (Calling the PartialMockTheClassUnderTest() method also creates or replaces the ClassUnderTest object.) Since you are explicitly creating it in your Establish block in the base class (_rhinoAutoMocker.ClassUnderTest), you would never be able to inject your own dependencies. Instead, you would only get the dependencies RhinoMocks created for you. This caused a little havoc for me when my specs were not behaving as expected when I was trying to partial mock the class as a convention.

  • Pingback: Awkward Coder » How to MSpec?