Пересмешивающий метод GetEnumerator () для типов IEnumerable <T> - PullRequest
12 голосов
/ 24 декабря 2008

Следующий тестовый пример терпит неудачу в насмешках на носорога:

[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 загрызть на самом деле работает. Как правило, когда вы перечисляете по IEnumerable, вы начинаем со свежего IEnumerator. В приведенном выше примере это выглядит как я мог бы повторно использовать тот же счетчик во второй раз, когда я вызывающая сумма, и если счетчик уже находится в конце последовательность, которая объясняет, почему второй вызов Sum () возвращает 0. Если это так, как я могу издеваться над GetEnumerator () в такой способ, которым он ведет себя так, как я этого хочу (например, новый перечислитель или тот же перечислитель сброшен в положение 0)?

Как бы вы изменили приведенный выше тестовый пример, чтобы второй вызов .Sum () фактически возвращал 6 вместо 0?

Ответы [ 3 ]

13 голосов
/ 30 декабря 2008

API WhenCalled () позволяет динамически разрешать возвращаемые значения.

Изменение контрольного примера на следующее позволит ему пройти:

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

Таким образом, вместо возврата того же перечислителя, поведение с заглушкой всегда будет возвращать новый перечислитель.

3 голосов
/ 24 декабря 2008

Заявление

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>().

0 голосов
/ 24 декабря 2008

Отказ от ответственности: я работаю в Typemock

Я не знаю, можете ли вы использовать Rhino для этого, но вы можете использовать Изолятор с 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); 
}

Для получения дополнительной информации см. Управление коллекциями в Руководстве разработчика.

...