views:

132

answers:

1

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?

+4  A: 

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.

Alexander Groß
Thanks Alexander, that makes perfect sense. I was hoping to avoid setting the context in each derived class but i guess thats not going to be possible
Kev Hunter
`Because` fields are executed in the correct order when you build up a class hierarchy. You can perfectly do that, but it's not good practice. Keep in mind that you want to keep as much information as possible close to the context that's being executed.
Alexander Groß