Question

I have a situation where I have an interface that exposes a method returning an IEnumerable

interface IIEnumerableProvider<T>
{
    IEnumerable<T> GetData();
}

I need to be able to create a stub of this that supports being called once, and when the resulting RangeIterator is enumerated a first time returns values defined by the stub, if enumerated a second time it will return a different set of values and so on for further enumerations.

Stubbing for a single enumeration is easy enough:

var stub = MockRepository.GenerateStub<IIEnumerableProvider<int>>();
stub.Stub(x => x.GetData()).Return(new []{1,2,3,4}).Repeat.Once();

Yet this does not cover the scenario where repeated enumerations of the result give different sets of values.

To give context I have a consumer method that calls the GetData method on the provider and uses the returned values twice, in error I enumerated the return twice, with unexpected behavior, but this was not highlighted in my unit tests on the consumer. As very simple consumer implementation to represent what I mean can be shown as:

class Consumer<T>
{
    bool UseProvider(IIEnumerableProvider<T> provider)
    {
        var rangeIterator = provider.GetData();
        var firstEnumeration = rangeIterator.ToArray();
        var secondEnumeration = rangeIterator.ToArray();
        return firstEnumeration.SequenceEquals(secondEnumeration);
    }
}

you would expect this to return false if the provider returned a value that is different every time it is enumerated, which is the case if we have a simple concrete implementation of IIEnumerableProvider:

public class RandomIntIEnumerableProvider : IIEnumerableProvider<int>
{
    private Random r = new Random();
    public IEnumerable<int> GetData()
    {
        return Enumerable.Range(0,10).Select(x => r.Next());
    }
}

So the question is how do I stub the provider in such a way as to produce well defined values on the first enumeration of the rangeIterator, but different (either defined as a list of sets of values or randomly determined). I need this in order to inject it into the consumer and ensure that the consumer is not performing multiple enumeration on the rangeIterator where it is a wrong to do so?

The naive approach of stubbing two return vales from the method does not work:

var stub = MockRepository.GenerateStub<IIEnumerableProvider<int>>();
stub.Stub(x => x.GetData()).Return(new []{1,2,3,4}).Repeat.Once();
stub.Stub(x => x.GetData()).Return(new []{5,6,7,8}).Repeat.Once();

as the GetData method is only called once, the set 1,2,3,4 is seen in the first and second enumerations.

Was it helpful?

Solution

Add another stub for the IEnumerable returned from GetData() and return that stub from the stub you described in the single enumeration approach in the question. With the first stub you can explicitly control the values that you return via WhenCalled().

var stubEnumerable = MockRepository.GenerateStub<IEnumerable<int>>();
stubEnumerable.Stub(x => x.GetEnumerator()).Return(null).WhenCalled(x => x.ReturnValue = ... );
var stub = MockRepository.GenerateStub<IIEnumerableProvider<int>>();
stub.Stub(x => x.GetData()).Return(stubEnumerable).Repeat.Once();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top