模拟IEnumerable 类型的GetEnumerator()方法

以下测试用例在rhino mocks中失败:

[TestFixture] public class EnumeratorTest { [Test] public void Should_be_able_to_use_enumerator_more_than_once() { var numbers = MockRepository.GenerateStub(); numbers.Stub(x => x.GetEnumerator()).Return(new List { 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 { } 

我认为这个测试用例有一些非常微妙的东西,我认为我不是在考虑Rhino Mocks stbing的实际效果。 通常,当您枚举IEnumerable时,您将从一个新的IEnumerator开始。 在上面的例子中,看起来我可以在第二次调用sum时重新使用相同的枚举器,并且如果枚举器已经在其序列的末尾,那么这将解释为什么第二次调用Sum()会返回0.如果是这种情况,我怎么能以这样的方式模拟GetEnumerator(),使其以我想要的方式运行(例如新的枚举器或同一个枚举器重置为0)?

你将如何修改上面的测试用例,以便第二个.Sum()调用实际上返回6而不是0?

WhenCalled()api允许您动态解析返回值。

将测试用例更改为以下内容将允许它通过:

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

因此,存根行为将始终返回一个新的枚举器,而不是返回相同的枚举器。

该声明

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

是完全相同的

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

在您的测试中,您告诉Rhino Mocks两次提供相同的IEnumerator实例enumerator 。 那不是你想要的。 IEnumerator单个实例仅适用于一个枚举,而不是两个枚举(通常不支持Reset() )。 您希望Rhino Mocks提供两个不同的IEnumerator实例,以便它们可以单独求和,就像对任何其他GetEnumerator()函数的任何调用一样。

免责声明:我在Typemock工作

我不知道你是否可以使用Rhino来做这个,但你可以使用Isolator和AAA API,它正是你想要的 –

 [Test] public void Should_be_able_to_use_enumerator_more_than_once() { var numbers = Isolate.Fake.Instance(); Isolate.WhenCalled(() => numbers).WillReturnCollectionValuesOf(new List{ 1, 2, 3 }); var sut = new ObjectThatUsesEnumerator(); var correctResult = sut.DoSomethingOverEnumerator2Times(numbers); Assert.IsTrue(correctResult); } 

有关详细信息,请参阅开发人员指南中的管理集合 。