Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking GetEnumerator() method of an IEnumerable<T> types

The following test case fails in rhino mocks:

[TestFixture] 
    public class EnumeratorTest 
    { 
        [Test] 
        public void Should_be_able_to_use_enumerator_more_than_once() 
        { 
            var numbers = MockRepository.GenerateStub<INumbers>(); 
            numbers.Stub(x => x.GetEnumerator()).Return(new List<int> 
{ 1, 2, 3 }.GetEnumerator()); 
            var sut = new ObjectThatUsesEnumerator(); 
            var correctResult = sut.DoSomethingOverEnumerator2Times 
(numbers); 
            Assert.IsTrue(correctResult); 
        } 
    } 
    public class ObjectThatUsesEnumerator 
    { 
        public bool DoSomethingOverEnumerator2Times(INumbers numbers) 
        { 
            int sum1 = numbers.Sum(); // returns 6 
            int sum2 = numbers.Sum(); // returns 0 =[ 
            return sum1 + sum2 == sum1 * 2; 
        } 
    } 
    public interface INumbers : IEnumerable<int> { } 

I think there is something very subtle about this test case, and I think it is from me not thinking through how Rhino Mocks stubbing actually works. Typically, when you enumerate over an IEnumerable, you are starting with a fresh IEnumerator. In the example above, it looks like I could be re-using the same enumerator the second time I am calling sum, and if the enumerator is already at the end of its sequence, that would explain why the second call to Sum() returns 0. If this is the case, how could I mock out the GetEnumerator() in such a way that it behaves in the way that I am wanting it to (e.g. new enumerator or same enumerator reset to position 0)?

How would you modify the above test case so that the second .Sum() call actually returns 6 instead of 0?

like image 226
programmer Avatar asked Dec 24 '08 13:12

programmer


2 Answers

The WhenCalled() api lets you dynamically resolve return values.

Changing the test case to the following will allow it to pass:

numbers.Stub(x => x.GetEnumerator())
                     .Return(null)
                     .WhenCalled(x => x.ReturnValue = 
                                    new List<int> { 1, 2, 3 }.GetEnumerator()
                                 );

So instead of returning the same enumerator, the stubbed behavior will always return a new enumerator.

like image 74
programmer Avatar answered Oct 22 '22 22:10

programmer


The statement

numbers.Stub(x => x.GetEnumerator()).Return(new List<int> { 1, 2, 3 }.GetEnumerator());

is identical to

var enumerator = new List<int> { 1, 2, 3 }.GetEnumerator();
numbers.Stub(x => x.GetEnumerator()).Return(enumerator);

In your test, you are telling Rhino Mocks to give the identical IEnumerator<int> instance enumerator twice. That's not what you intended. A single instance of IEnumerator<int> is good only for one enumeration, not two enumerations (Reset() is not supported, typically). You intended Rhino Mocks to give two different instances of IEnumerator<int>, so that they can be summed separately, just as any call to any other GetEnumerator<int>() function would do.

like image 3
yfeldblum Avatar answered Oct 22 '22 22:10

yfeldblum