Wrapping up the StructureMap Automocking Container

UPDATE: I apologize…the original wrapper did not allow injected dependencies. I have fixed this bug and updated the sample.

I have been using StructureMap.AutoMocking with MSpec (Machine.Specifications) and Rhino.Mocks for a few months now. Although I am very comfortable with the patterns that emerge from using the frameworks together, introducing StructureMap.Automocking to other developers is sometimes challenging.

When viewing a specification or test set up with the Rhino.Mocks or Moq automocking container, what is being tested is not readily apparent. Once the concept is explained and demonstrated, these same developers seem to have no issue with maintaining the specifications/tests.

I decided to wrap the details of the container to make the intention clearer and explanation easier. I have started using this wrapper in my projects, and have found it makes building up specifications more efficient in my daily coding.

Let me know whether this is useful to you, or how it could be made more useful.

The Examples

Let’s contrive an example. We’ll say we have a coffee machine that grinds its own beans before brewing. However, the hopper has to have beans before starting the grind. Here is the basic specification:

Action/Behavior

  • Prepare coffee grounds for 12 cups of coffee

Expectations

  • Should check that hopper has beans (mock returns true)
  • Because hopper has beans, should ask grinder to grind enough beans for 12 cups of coffee

We could start without a container, using Rhino.Mocks to build up the CoffeeMachine dependencies manually:

    [Subject(typeof(CoffeeMachine),"using vanilla Rhino.Mocks")]
public class Example01_when_preparing_coffee_grounds
{
Establish context = () =>
{
Grinder = MockRepository.GenerateMock<IGrinder>();
Hopper = MockRepository.GenerateMock<IHopper>();
Hopper.Expect(x => x.HasBeans()).Return(true);
CoffeeMachine = new CoffeeMachine(Grinder, Hopper);
};

Because of = () => _coffeeMachine.PrepareCoffeeGrounds(12);

It should_check_if_hopper_has_beans = () => Hopper.VerifyAllExpectations();
It should_grind_coffee = () => Grinder.AssertWasCalled(x => x.Grind(12));

static IGrinder Grinder;
static IHopper Hopper;
static CoffeeMachine CoffeeMachine;
}

For those of you not familiar with the MSpec style, please refer to the following posts:

Here is the same specification using the RhinoAutoMocker class provided by StructureMap.AutoMocking:

    [Subject(typeof(CoffeeMachine),"using RhinoAutoMocker<CoffeeMachine>")]
public class Example02_when_preparing_coffee_grounds
{
Establish context = () =>
{
CoffeeMachine = new RhinoAutoMocker<CoffeeMachine>();
Grinder = CoffeeMachine.Get<IGrinder>();
Hopper = CoffeeMachine.Get<IHopper>();
Hopper.Expect(x => x.HasBeans()).Return(true);
};

Because of = () => v.ClassUnderTest.PrepareCoffeeGrounds(12);

It should_check_if_hopper_has_beans = () => Hopper.VerifyAllExpectations();
It should_grind_coffee = () => Grinder.AssertWasCalled(x => x.Grind(12));

static IGrinder Grinder;
static IHopper Hopper;
static RhinoAutoMocker<CoffeeMachine> CoffeeMachine;
}

Although I think this looks cleaner, the initialization of CoffeeMachine causes some confusion. Unfortunately, a common question would be, “Is RhinoAutoMocker creating a mocked version of CoffeeMachine?" Well, not exactly.

RhinoAutoMocker is an implementation of the AutoMocker base class which uses StructureMap to fill dependencies. The RhinoAutoMocker implementation of the container uses Rhino.Mocks to generate all of dependencies of the target class, whereas the MoqAutoMocker implementation uses Moq. (Optionally, the target class can be partially mocked in order to further isolate behavior.) We then retrieve the class under test (in this case, CoffeeMachine) and its dependencies (IGrinder and IHopper) from the container for use.

If you do not need to setup any expectations in the specification, there is no need to retrieve the dependency from the container. However, in this specification, we need both dependencies.

(Incidentally, I do not like the ClassUnderTest name. I may end up changing this to Instance, ClassInstance, TargetClass, or something similar. Suggestions?)

By moving the initialization of the RhinoAutoMocker to a base class, some of the chattiness of the context can be hidden.

    
[Subject(typeof(CoffeeMachine),"using RhinoAutoMocker base")]
public class Example03_when_preparing_coffee_grounds : with_rhinoautomocker
{
Establish context = () => Hopper.Expect(x => x.HasBeans()).Return(true);

Because of = () => CoffeeMachine.ClassUnderTest.PrepareCoffeeGrounds(12);

It should_check_if_hopper_has_beans = () => Hopper.VerifyAllExpectations();
It should_grind_coffee = () => Grinder.AssertWasCalled(x => x.Grind(12));

}

[Subject("using RhinoAutoMocker<T>")]
public class with_rhinoautomocker
{
protected static IGrinder Grinder;
protected static IHopper Hopper;
protected static RhinoAutoMocker<CoffeeMachine> CoffeeMachine;

Establish context = () =>
{
CoffeeMachine = new RhinoAutoMocker<CoffeeMachine>();
Grinder = CoffeeMachine.Get<IGrinder>();
Hopper = CoffeeMachine.Get<IHopper>();
};
}

However, we are still initializing and accessing the class in a less than optimal manner.To make the usage of the container a little more seamless, I decided to create a wrapper and factory for the AutoMocker. Setup of the dependencies and expectations are done through an AutoMocker wrapper object. The instance of the class under test is accessed through a ClassUnderTest object.

The Result

The specification (and base class) now look like this:

    
[Subject(typeof(CoffeeMachine),"using coffee machine base")]
public class Example05_when_preparing_coffee_grounds : with_coffee_machine
{
Establish context = () => Hopper.Expect(x => x.HasBeans()).Return(true);

Because of = () => ClassUnderTest.PrepareCoffeeGrounds(12);

It should_check_if_hopper_has_beans = () => Hopper.VerifyAllExpectations();
It should_grind_coffee = () => Grinder.AssertWasCalled(x => x.Grind(12));
}

[Subject("using SpecificationFor<CoffeeMachine>")]
public class with_coffee_machine : SpecificationFor<CoffeeMachine>
{
protected static IGrinder Grinder;
protected static IHopper Hopper;

public with_coffee_machine
{
Grinder = AutoMocker.Get<IGrinder>();
Hopper = AutoMocker.Get<IHopper>();
}
}

As you can see, the base class inherits from SpecificationFor. The default constructor uses Rhino.Mocks(MockMode.AAA) to generate the dependencies.  It then exposes a AutoMocker object, which is just the wrapper around the AutoMocker base class. It also exposes a ClassUnderTest object. If I didn’t need to use the dependencies, I could make the _Example05_when_preparing_coffee_grounds_ class inherit from _SpecificationFor _instead of inheriting from the base class_._

The Code

I have included the code for the wrapper below for your review. The code and six examples are available on my Google code repository. The repository also includes an example using the MoqAutoMocker. All the examples have the same assertions, but are built up using the different techniques.

    namespace CodeProgression.Framework.Testing
{
public abstract class SpecificationFor<T> where T: class
{
protected static ClassUnderTest<T> Factory;

// UPDATE 2009-12-21:
// Moved initialization here
protected static T ClassUnderTest {get {return AutoMocker.Instance;} }

protected SpecificationFor()
{
AutoMocker = AutoMockFactory.CreateTarget<T>();

// UPDATE 2009-12-21:
// Initializing here prevented injected dependencies!
// AutoMocker.PartialMockTheClassUnderTest();
// ClassUnderTest = AutoMocker.Instance;
}

protected SpecificationFor(AutoMockType type)
{
AutoMocker = AutoMockFactory.CreateTarget<T>(type);

// UPDATE 2009-12-21:
// Initializing here prevented injected dependencies!
// AutoMocker.PartialMockTheClassUnderTest();
// ClassUnderTest = AutoMocker.Instance;
}
}

public static class AutoMockFactory
{
public static ClassUnderTest<TARGETCLASS> CreateTarget<TARGETCLASS>() where TARGETCLASS : class
{
return CreateTarget<TARGETCLASS>(AutoMockType.RhinoMocksAAA);
}

public static ClassUnderTest<TARGETCLASS> CreateTarget<TARGETCLASS>(AutoMockType framework) where TARGETCLASS : class
{
AutoMocker<TARGETCLASS> mocker;
ServiceLocator serviceLocator;
switch (framework)
{
case AutoMockType.RhinoMocksAAA:
mocker = new RhinoAutoMocker<TARGETCLASS>(MockMode.AAA);
serviceLocator = new RhinoMocksAAAServiceLocator();
break;
case AutoMockType.RhinoMocksClassic:
mocker = new RhinoAutoMocker<TARGETCLASS>(MockMode.RecordAndReplay);
serviceLocator = new RhinoMocksClassicServiceLocator();
break;
case AutoMockType.Moq:
mocker = new MoqAutoMocker<TARGETCLASS>();
serviceLocator = new MoqServiceLocator();
break;
default:
throw new ArgumentOutOfRangeException("framework");
}
return new ClassUnderTest<TARGETCLASS>(mocker, serviceLocator);
}
}

public enum AutoMockType
{
Moq,
RhinoMocksAAA,
RhinoMocksClassic
}

public class ClassUnderTest<TARGETCLASS> where TARGETCLASS : class
{
readonly IAutoMocker<TARGETCLASS> _mocker;
readonly ServiceLocator _serviceLocator;

public ClassUnderTest(IAutoMocker<TARGETCLASS> mocker, ServiceLocator serviceLocator)
{
_mocker = mocker;
_serviceLocator = serviceLocator;
}

public AutoMockedContainer Container
{
get { return _mocker.Container; }
}

public TARGETCLASS Instance
{
get { return _mocker.ClassUnderTest; }
}

public void MockObjectFactory()
{
_mocker.MockObjectFactory();
}

public void PartialMockTheClassUnderTest()
{
_mocker.PartialMockTheClassUnderTest();
}

public T Get<T>() where T : class
{
return _mocker.Get<T>();
}

public void Inject(Type pluginType, object stub)
{
_mocker.Inject(pluginType, stub);
}

public void Inject<T>(T target)
{
_mocker.Inject(target);
}

public T AddAdditionalMockFor<T>() where T : class
{
return _mocker.AddAdditionalMockFor<T>();
}

public void UseConcreteClassFor<T>()
{
_mocker.UseConcreteClassFor<T>();
}

public T[] CreateMockArrayFor<T>(int count) where T : class
{
return _mocker.CreateMockArrayFor<T>(count);
}

public void InjectArray<T>(T[] stubs)
{
_mocker.InjectArray(stubs);
}

public T Mock<T>() where T : class
{
return _serviceLocator.Service<T>();
}
public object Mock(Type serviceType)
{
return _serviceLocator.Service(serviceType);
}
public T PartialMock<T>() where T : class
{
return _serviceLocator.PartialMock<T>();
}
}
}
comments powered by Disqus