Как изменить возвращаемое значение метода во время выполнения модульного теста

Я пытаюсь разработать модульный тест для проверки шаблона повторения цикла. Единственный способ, которым я могу думать, - это изменить то, что возвращает метод, встроенный в сердце цикла повторения, в середине теста.

Например... Я хотел бы выдать исключение для конкретного метода в течение первых 5 секунд теста. И затем остановите это исключение от выдачи и фактически ответьте с некоторыми действительными данными после этой точки.

За первые 5 секунд:

service.MethodToRetry(Arg.Any<string>()).ThrowsForAnyArgs(new Exception());

А затем после этого исключение условие удаляется и MethodToRetry() завершается нормально.

Возможно ли это или я поступаю совершенно неправильно? Я работаю в C# с Xunit и Nsubstitute.

2 ответа

Решение

Примечание: тесты здесь должны продемонстрировать поведение NSubstitute. В реальных тестах мы не будем тестировать замену. :)

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

    [Test]
    public void StubMultipleCalls() {
        Func<string> throwEx = () => { throw new Exception(); };
        var sub = Substitute.For<IThingoe>();
        // Stub method to fail twice, then return valid data
        sub.MethodToRetry(Arg.Any<string>())
           .Returns(x => throwEx(), x => throwEx(), x => "works now");

        // The substitute will then act like this:
        Assert.Throws<Exception>(() => sub.MethodToRetry("")); 
        Assert.Throws<Exception>(() => sub.MethodToRetry("")); 
        Assert.AreEqual("works now", sub.MethodToRetry(""));
        // Will continue returning last stubbed value...
        Assert.AreEqual("works now", sub.MethodToRetry(""));
        Assert.AreEqual("works now", sub.MethodToRetry(""));
    }

Другой вариант заключается в том, чтобы поставить условие в то время, когда вы заглушаете вызов:

    [Test]
    public void StubWithCondition() {
        var shouldThrow = true;

        var sub = Substitute.For<IThingoe>();
        sub.MethodToRetry(Arg.Any<string>()).Returns(x => {
            if (shouldThrow) {
                throw new Exception();
            }
            return "works now";
        });

        Assert.Throws<Exception>(() => sub.MethodToRetry(""));
        shouldThrow = false; // <-- can alter behaviour by modifying this variable
        Assert.AreEqual("works now", sub.MethodToRetry(""));
    }

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

    [Test]
    public void ReplaceLambda() {
        Func<string> methodToRetry = () => { throw new Exception(); };

        var sub = Substitute.For<IThingoe>();
        sub.MethodToRetry(Arg.Any<string>()).Returns(x => methodToRetry());

        Assert.Throws<Exception>(() => sub.MethodToRetry(""));
        methodToRetry = () => "works now";
        Assert.AreEqual("works now", sub.MethodToRetry(""));
    }

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

Во-первых, я не вижу конкретных реализаций, поэтому я изложу это в общем.

Мысли:

  1. Поскольку вы хотите протестировать "шаблон повторения цикла", я предполагаю, что у вас есть логика части "5 секунд ожидания". Эта логика должна быть инъекционным вызовом, поэтому в своем тесте вы можете проверить, был ли он вызван или нет. ( http://nsubstitute.github.io/help/received-calls/)
  2. Часть ожидания не должна быть в ваших тестах, потому что ваш метод должен ждать в повторных шагах, а не тестах.
Другие вопросы по тегам