views:

47

answers:

2

So this is something I guess many people want to do, mock a collection. In the past with Rhino I have done this with something like:

var col_mock = MockRepository.GenerateMock<ICustomCollection>(); // returns ICustom let's say
List<ICustom> col_real = new List<ICustom>();
col_real.Add(custom_mock1);
col_real.Add(custom_mock2);
col_real.Add(custom_mock3);
col_mock.Stub(x => x.GetEnumerator()).Return(col_real.GetEnumerator());

So yup this works fine, when you foreach col_mock you get the mocked (custom_mock1 etc.) objects back. Great! We have successfully mocked a custom collection by using a typed list to actually store a load of mocked objects.

The problem is, you can only do this once! you can only foreach this collection once. Does anyone know (without creating an actual custom collection...) how I can achieve the mocking of a custom collection which can be iterated more than once?

+2  A: 

The problem is that the enumerator is only instantiated once, when you call Return. Then it returns the same instance which is already at the end of the list after the first foreach.

You need to instantiate a new enumerator each time when GetEnumerator is called. You could use WhenCalled to do so.

Return is still needed, because Rhino Mocks will complain when it is missing. But it doesn't matter what you pass as argument.

[TestMethod]
public void GetEnumerator()
{
    IList<int> col_mock = MockRepository.GenerateMock<IList<int>>();
    List<int> col_real = new List<int>();
    col_real.Add(1);
    col_real.Add(2);
    col_real.Add(3);
    col_mock
        .Stub(x => x.GetEnumerator())
        // create new enumerator instance for each call
        .WhenCalled(call => call.ReturnValue = col_real.GetEnumerator())
        .Return(null) // is ignored, but needed for Rhinos validation
        .Repeat.Any();

    foreach (int i in col_mock)
    {
    }

    int count = 0;
    foreach (int i in col_mock)
    {
        count++;
    }
    Assert.AreSame(3, count);
}
Stefan Steinegger
Thanks! and well done, actually the Return(null) and Repeat.Any calls don't seem to be required. I hadn't used the WhenCalled before, this is exactly what I wanted to do.
MRAH
+1  A: 

So this is something I guess many people want to do, mock a collection.

Actually, no. The point of a mock is to quickly conjure up an object with a specific interface and behavior that you have full control over, so that you can exercise your class under test.

But in the case where you need an ICollection<Foo>, you can typically just create a List<Foo> instance. You can populate a list in your unit test any way you want; you already have full control.

Or suppose you need a IEnumerable<Foo> with more interesting behavior that you can't produce with a list, like an infinite enumerable or an enumerable that throws exceptions. This can be done by defining an iterator method in your unit test class with the yield keyword. Again, a mock is not necessary.

Wim Coenen
I have to disagree with you here. When your collection is not an ICollection<T> (or derivations of like IList<T> etc) but actually an oldschool custom collection object which you cannot change but must use (as in my case); then you will need to mock the collection object itself. Some of the interfaces I am using have not been refactored since .Net 1.1. Thanks for the input though.
MRAH