RhinoMocks - насмешливый метод, чье возвращаемое значение изменяется (даже если передан один и тот же параметр) с несколькими вызовами

Я хочу выяснить, как я могу смоделировать метод, который возвращает другое значение во второй раз, когда он вызывается в первый раз. Например, что-то вроде этого:

public interface IApplicationLifetime
{
    int SecondsSinceStarted {get;}
}

[Test]
public void Expected_mock_behaviour()
{
    IApplicationLifetime mock = MockRepository.GenerateMock<IApplicationLifetime>();

    mock.Expect(m=>m.SecondsSinceStarted).Return(1).Repeat.Once();
    mock.Expect(m=>m.SecondsSinceStarted).Return(2).Repeat.Once();

    Assert.AreEqual(1, mock.SecondsSinceStarted);
    Assert.AreEqual(2, mock.SecondsSinceStarted);
}

Есть ли что-нибудь, что делает это возможным? Помимо реализации подпрограммы для геттера, который реализует конечный автомат?

Приветствия, ребята,

Alex

 AlexC23 мая 2012 г., 17:14
@lazyberezovsky При первом вызове m.SecondsSinceStarted возвращается 2, а не 1, как ожидалось
 Maciej23 мая 2012 г., 15:44
Посмотрите на заказанные и неупорядоченные макеты:ayende.com/wiki/Rhino%20Mocks%20Ordered%20and%20Unordered.ashx
 Sergey Berezovskiy23 мая 2012 г., 17:29
@AlexC вы можете предоставить реальный тестовый код? Так какRepeat.Once() вернет 1 во время первого звонка
 AlexC23 мая 2012 г., 18:12
@lazyberezovsky Код выше реален. Если я запускаю его с последней сборкой RhinoMocks (после замены m.SecondsSinceStarted на mock.SecondsSinceStarted внутри Asserts - глупая ошибка), то тест завершается с: Expected: 1 Но было: 2
 Sergey Berezovskiy23 мая 2012 г., 16:56
Что не так с кодом, который вы предоставили?Repeat.Once() должно сработать

Ответы на вопрос(3)

который возвращает метод? Если это так, я бы попытался абстрагироваться от времени, чтобы вы имели прямой контроль над ним в своих тестах.

Что-то вроде интерфейса под названием ITimeProvider с элементом GetElapsedTime (), а затем создайте реализацию по умолчанию для использования в вашем приложении.

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

 AlexC23 мая 2012 г., 17:22
Мой вопрос был больше о том, поддерживает ли RhinoMocks этот тип взаимодействия с генерируемыми макетами / заглушками, и меньше о том, как я мог бы обойти это, если бы его не поддерживали. Спасибо хоть.
 23 мая 2012 г., 17:18
Может ли ITimeProvider быть зависимым от свойства? Таким образом, возможность вводить новые макеты перед каждым вызовом? Тем не менее, если вам не пора, мой ответ, вероятно, бесполезен ...
 AlexC23 мая 2012 г., 17:12
Действительно, время было просто удобным примером без полного описания того, что я пытаюсь проверить. Тем не менее, даже если бы я хотел смоделировать ITimeProvider, у которого был метод GetElapsedTime (), проблема все равно существовала бы, если бы я хотел написать модульный тест, который дважды вызывал GetElapsedTime (), и хотел, чтобы мой макет ITimeProvider возвращал другой время для каждого звонка.
Решение Вопроса

.WhenCalled метод. Обратите внимание, что вам все еще нужно предоставить значение через.Return метод, однако Rhino будет просто игнорировать его, еслиReturnValue изменяется от вызова метода:

int invocationsCounter = 1;
const int IgnoredReturnValue = 10;
mock.Expect(m => m.SecondsSinceLifetime)
    .WhenCalled(mi => mi.ReturnValue = invocationsCounter++)
    .Return(IgnoredReturnValue);

Assert.That(mock.SecondsSinceLifetime, Is.EqualTo(1));
Assert.That(mock.SecondsSinceLifetime, Is.EqualTo(2));

Edit:

Копаться немного больше, кажется, что.Repeat.Once() does действительно работают в этом случае и могут быть использованы для достижения того же результата:

mock.Expect(m => m.SecondsSinceStarted).Return(1).Repeat.Once();
mock.Expect(m => m.SecondsSinceStarted).Return(2).Repeat.Once();
mock.Expect(m => m.SecondsSinceStarted).Return(3).Repeat.Once();

Вернет 1, 2, 3 при последовательных звонках.

 23 мая 2012 г., 16:46
Как я понимаю, для чего-то кроме последовательных чисел он должен создавать массив и возвращать значения по индексу?
 AlexC23 мая 2012 г., 17:08
Я согласен, что этот метод работает, но это по сути то же самое, что и реализация конечного автомата, о котором я упоминал, что я пытаюсь избежать (то есть, если я хочу вернуть 11 при втором вызове, тогда мне нужно что-то сделать например & quot; if (invocationsCounter == 1) return 1; еще if (invocationsCounter == 2) return 11; & quot; очень похоже на конечный автомат.
 AlexC23 мая 2012 г., 18:20
Кажется, проблема была в неосторожности с моей стороны - тесты проходят, если я запускаю, но не дают результатов, если я прохожу, вероятно, где-то оценивая свойство для окна наблюдения. Ответ принят, т.е
 AlexC23 мая 2012 г., 17:25
Re: ваше редактирование. Не уверен, что это какая-то функциональность, которая изменилась в версии RhinoMocks, которую я использую, и той, которую вы используете, но я запустил тест, который находится в моем первоначальном вопросе, и могу сказать вам, что он не работает (т.е. первый вызов в SecondsSinceStarted возвращает 2). У вас есть ссылка на то, где вы обнаружили, что это работает?

mock.Expect(m=>m.SecondsSinceStarted).Return(1).Repeat.Once();
mock.Expect(m=>m.SecondsSinceStarted).Return(2).Repeat.Once();

Это вернется1 во время первого звонка и2 во время второго звонка. По крайней мере, на RhinoMocks 3.6.0.0

 AlexC23 мая 2012 г., 18:16
Это, безусловно, противоречит тому, что я вижу, когда я запускаю тест выше
 AlexC23 мая 2012 г., 18:21
Как упоминалось в принятом ответе, это правильно, мой тест только провалился, когда я проходил и оценивал что-то непреднамеренно. Upvoted.

Ваш ответ на вопрос