Mocking метод GetEnumerator() типов IEnumerable <T>
В носорогах из носорога возникает следующий тестовый случай:
[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> { }
Я думаю, что в этом тестовом случае есть что-то очень тонкое, и я
думаю, что это от меня, не думая, как Rhino Mocks stubbing
фактически работает. Как правило, когда вы перечислите IEnumerable, вы
начинаются со свежего IEnumerator. В приведенном выше примере он выглядит
как я мог бы повторно использовать тот же счетчик во второй раз, когда я
вызывающая сумма, и если счетчик уже находится в конце
последовательность, которая объясняет, почему второй вызов Sum() возвращает 0.
Если это так, то как я мог высмеивать GetEnumerator() в таких
способ, которым он ведет себя так, как я этого хочу (например, новый
перечислитель или тот же счетчик reset в позицию 0)?
Как бы вы изменили вышеуказанный тестовый пример, так что второй вызов .Sum() фактически возвращает 6 вместо 0?
Ответы
Ответ 1
Функция WhenCalled() api позволяет динамически разрешать возвращаемые значения.
Изменение тестового примера на следующее позволит ему пройти:
numbers.Stub(x => x.GetEnumerator())
.Return(null)
.WhenCalled(x => x.ReturnValue =
new List<int> { 1, 2, 3 }.GetEnumerator()
);
Поэтому вместо того, чтобы возвращать тот же счетчик, зашитое поведение всегда будет возвращать новый счетчик.
Ответ 2
Утверждение
numbers.Stub(x => x.GetEnumerator()).Return(new List<int> { 1, 2, 3 }.GetEnumerator());
совпадает с
var enumerator = new List<int> { 1, 2, 3 }.GetEnumerator();
numbers.Stub(x => x.GetEnumerator()).Return(enumerator);
В своем тесте вы рассказываете Rhino Mocks, чтобы дать идентичный IEnumerator<int>
экземпляр enumerator
дважды. Это не то, что вы намеревались. Один экземпляр IEnumerator<int>
хорош только для одного перечисления, а не для двух перечислений (Reset()
обычно не поддерживается). Вы назначили Rhino Mocks, чтобы дать два разных экземпляра IEnumerator<int>
, чтобы их можно было суммировать отдельно, так же, как и любой вызов любой другой функции GetEnumerator<int>()
.
Ответ 3
Отказ от ответственности: я работаю в Typemock
Я не знаю, можете ли вы использовать Rhino для этого, но вы можете использовать Isolator с AAA API, который имеет именно то, что вы ищете -
[Test]
public void Should_be_able_to_use_enumerator_more_than_once()
{
var numbers = Isolate.Fake.Instance<INumbers>();
Isolate.WhenCalled(() => numbers).WillReturnCollectionValuesOf(new List<int>{ 1, 2, 3 });
var sut = new ObjectThatUsesEnumerator();
var correctResult = sut.DoSomethingOverEnumerator2Times(numbers);
Assert.IsTrue(correctResult);
}
Подробнее см. Управление коллекциями в Руководстве разработчика.