Как изменить поведение заглушек?

Могу ли я изменить поведение заглушки во время выполнения? Что-то вроде:

    public interface IFoo { string GetBar(); }
    [TestMethod]
    public void TestRhino()
    {
        var fi = MockRepository.GenerateStub<IFoo>();
        fi.Stub(x => x.GetBar()).Return("A");
        Assert.AreEqual("A", fi.GetBar());
        fi.Stub(x => x.GetBar()).Return("B");
        Assert.AreEqual("B", fi.GetBar());    // Currently fails here
    }

Мой пример кода все еще не работает в данной строке, fi.GetBar() все еще возвращает "A".

Или есть еще один трюк для моделирования заглушек, чье поведение меняется со временем? Я бы предпочел не использовать fi.Stub(...).Do(...).

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

Ответ 1

Внимание

Изменение поведения заглушек - это запах кода!

Обычно это указывает на то, что ваши юнит-тесты слишком сложны, их трудно понять и они хрупки, легко ломаются при правильных изменениях тестируемого класса.

Отъезд:

  • [xUnit Test Patterns] [1]
  • [Искусство тестирования единиц] [2]

Итак, пожалуйста: используйте это решение, если вы не можете его избежать. В моих глазах эта статья граничит с плохими советами - однако есть редкие ситуации, в которых вы действительно нуждаетесь.


А, я сам это понял. Rhino поддерживает режим записи/воспроизведения. Хотя синтаксис AAA всегда сохраняет объекты в режиме воспроизведения, мы можем переключиться на запись и вернуться к воспроизведению, чтобы очистить поведение заглушки.

Это выглядит немного хакерским, однако...

    public interface IFoo { string GetBar(); }
    [TestMethod]
    public void TestRhino()
    {
        var fi = MockRepository.GenerateStub<IFoo>();
        fi.Stub(x => x.GetBar()).Return("A");
        Assert.AreEqual("A", fi.GetBar());

        // Switch to record to clear behaviour and then back to replay
        fi.BackToRecord(BackToRecordOptions.All);
        fi.Replay();

        fi.Stub(x => x.GetBar()).Return("B");
        Assert.AreEqual("B", fi.GetBar());
    }

Update:

Я буду использовать это в будущем, так что все выглядит немного лучше:

internal static class MockExtension {
    public static void ClearBehavior<T>(this T fi)
    {
        // Switch back to record and then to replay - that 
        // clears all behaviour and we can program new behavior.
        // Record/Replay do not occur otherwise in our tests, that another method of
        // using Rhino Mocks.

        fi.BackToRecord(BackToRecordOptions.All);
        fi.Replay();
    }
}