Search code examples
unit-testingrhino-mocksbddmspec

When using a mocking framework and MSPEC where do you set your stubs


I am relatively new to using MSpec and as I write more and more tests it becomes obvious to reduce duplication you often have to use a base class for your setup as per Rob Conery's article

I am happy with using the AssertWasCalled method to verify my expectations, but where do you set up a stub's return value, I find it useful to set the context in the base class injecting my dependencies but that (I think) means that I need to set my stubs up in the Because delegate which just feels wrong.

Is there a better approach I am missing?


Solution

  • The initialization/setup of stubs belongs to the arrange phase. The arrange phase is used to get the system into a known state before you exercise it.

    In MSpec, the arrange phase is performed in Establish fields. For example:

    public class When_the_temperature_threshold_is_reached
    {
        static ITemperatureSensor Sensor;
        static Threshold Threshold;
    
        Establish context = () =>
            {
                Sensor = MockRepository.GenerateStub<ITemperatureSensor>();
                Sensor
                    .Stub(x => x.GetTemperature())
                    .Return(42);
    
                Threshold = new Threshold(Sensor);
            };
    
        Because of = () => Reached = Threshold.IsReached(40);
    
        It should_report_that_the_threshold_was_reached =
            () => Reached.ShouldBeTrue();
    }
    

    When you write more tests using that kind of ITemperatureSensor, you should extract a base class that does complicated or repeated setup.

    public abstract class TemperatureSpecs
    {
        protected static ITemperatureSensor CreateSensorAlwaysReporting(int temperature)
        {
            var sensor = MockRepository.GenerateStub<ITemperatureSensor>();
            sensor
                .Stub(x => x.GetTemperature())
                .Return(temperature);
    
            return sensor;
        }
    }
    
    public class When_the_temperature_threshold_is_reached : TemperatureSpecs
    {
        // Everything else cut for brevity.
        Establish context = () =>
            {
                Sensor = CreateSensorAlwaysReporting(42);
    
                Threshold = new Threshold(Sensor);
            };
    }
    

    This gives you the advantage that you can influence the stub's return value from the context itself: You do this by keeping as much information as possible local to the context and provide a good name for the "setup" method in the base class.

    There is no need to specifiy or expect anything stub-related in Because. When Because is run, your system should be in a state where it can be exercised without further preparation.