Подтвердить что ты не робот

Reset проверка в Moq?

Настройка как таковая:

public interface IFoo
{
    void Fizz();
}

[Test]
public void A()
{
    var foo = new Mock<IFoo>(MockBehavior.Loose);

    foo.Object.Fizz();

    foo.Verify(x => x.Fizz());

    // stuff here

    foo.Verify(x => x.Fizz(), Times.Never()); // currently this fails
}

В принципе, я хотел бы ввести код в // stuff here, чтобы сделать проход foo.Verify(x => x.Fizz(), Times.Never()).

И поскольку это, вероятно, представляет собой мокси/модульное тестирование, мое оправдание заключается в том, что я могу сделать что-то вроде этого:

[Test]
public void Justification()
{
    var foo = new Mock<IFoo>(MockBehavior.Loose);
    foo.Setup(x => x.Fizz());

    var objectUnderTest = new ObjectUnderTest(foo.Object);

    objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup

    foo.Verify(x => x.Fizz());

    // reset the verification here

    objectUnderTest.DoStuffToPushIntoState2(); // more lines of code

    foo.Verify(x => x.Fizz(), Times.Never());
}

В принципе, у меня есть объект состояния, где справедливая часть работы (как с точки зрения создания различных макетных объектов, так и с другими faffing around) требует, чтобы она входила в State1. Затем я хочу проверить переход из State1 в State2. Вместо дублирования или абстрагирования кода я бы предпочел просто повторно использовать тест State1, перетащить его в State2 и выполнить мои Asserts - все, что я могу сделать, кроме проверочных вызовов.

4b9b3361

Ответ 1

Я не думаю, что вы можете reset издеваться над этим. Вместо этого, если вы знаете, что Fizz следует вызывать один раз при переходе в состояние 1, вы можете выполнить свои проверки следующим образом:

objectUnderTest.DoStuffToPushIntoState1();
foo.Verify(x => x.Fizz(), Times.Once());  // or however many times you expect it to be called

objectUnderTest.DoStuffToPushIntoState2();
foo.Verify(x => x.Fizz(), Times.Once());

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

Изменить

В качестве примера я проверил следующий код с xUnit:

[Fact]
public void Test()
{
    var foo = new Mock<IFoo>(MockBehavior.Loose);

    foo.Object.Fizz();
    foo.Verify(x => x.Fizz(), Times.Once(), "Failed After State 1");

    // stuff here
    foo.Object.Fizz();
    foo.Verify(x => x.Fizz(), Times.Once(), "Failed after State 2"); 
}

Этот тест завершился неудачей с сообщением "Ошибка после состояния 2". Это имитирует, что произойдет, если ваш метод, который подталкивает foo в State 2, вызывает Fizz. Если это произойдет, второй Verify завершится с ошибкой.

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

Ответ 2

Я думаю, что после того, как этот пост был создан, они добавили функциональность, которую запросил OP, существует метод расширения Moq, называемый Moq.MockExtensions.ResetCalls().

С помощью этого метода вы можете сделать именно то, что вам нужно, как показано ниже:

[Test]
public void Justification()
{
    var foo = new Mock<IFoo>(MockBehavior.Loose);
    foo.Setup(x => x.Fizz());

    var objectUnderTest = new ObjectUnderTest(foo.Object);

    objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup

    foo.Verify(x => x.Fizz());

    foo.ResetCalls(); // *** Reset the verification here with this glorious method ***

    objectUnderTest.DoStuffToPushIntoState2(); // more lines of code

    foo.Verify(x => x.Fizz(), Times.Never());
}

Ответ 3

В случае, если люди MOQ слушают, у меня есть случай, когда мне нужно reset Verify. У меня около 200 тестов в тестовом классе NUnit. SUT довольно сложно настроить (нам нужны 6 mocks и другой код настройки). В нашем тестовом комплекте у нас около 7000 тестов, и у нас возникают проблемы с памятью, поэтому я пытаюсь устранить требования mem. Я хочу переместить все ложные вызовы в TestFixtureSetUp вместо SetUp. Это работало, за исключением нескольких случаев, которые проверяют, был ли метод вызван ровно один раз. Второй тест, который проверял Times.Exactly(1) теперь терпит неудачу. Если бы я мог reset Verify, я мог бы повторно использовать Mock и использовать mem гораздо разумнее.

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

Ответ 4

Я также был свидетелем Times.Exactly(1) ошибка проверки в модульных тестах с использованием MoQ, с сообщением об ошибке "было вызвано 2 раза". Я рассматриваю это как ошибку в MoQ, так как я ожидал бы чистых состояний при каждом тестировании.

Моя работа заключалась в назначении нового тестового объекта и тестовой цели в тестовой настройке.

private Mock<IEntityMapper> entityMapperMock;
private OverdraftReportMapper target;

[SetUp]
public void TestSetUp()
{
  entityMapperMock = new Mock<IEntityMapper>();
  target = new OverdraftReportMapper(entityMapperMock.Object);
} 

Ответ 5

Вы можете использовать метод Callback вместо Verify и подсчитывать вызовы.

Это показано на странице Moq Quick Start, таким образом:

// returning different values on each invocation
var mock = new Mock<IFoo>();
var calls = 0;
mock.Setup(foo => foo.GetCountThing())
    .Returns(() => calls)
    .Callback(() => calls++);
// returns 0 on first invocation, 1 on the next, and so on
Console.WriteLine(mock.Object.GetCountThing());

Ответ 6

В зависимости от того, какую версию Mock вы используете, я точно знаю, что мы можем это сделать

someMockObject.ResetCalls();

Ответ 7

Это действительно unit test злоупотребление, поскольку вы проверяете две вещи в одном тесте. Ваша жизнь была бы намного проще, если бы вы взяли инициализацию ObjectUnderTest из теста и в общий метод настройки. Затем ваши тесты становятся более читабельными и независимыми друг от друга.

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

ObjectUnderTest _objectUnderTest;

[Setup] //Gets run before each test
public void Setup() {
    var foo = new Mock<IFoo>(); //moq by default creates loose mocks
    _objectUnderTest = new ObjectUnderTest(foo.Object);
}
[Test]
public void DoStuffToPushIntoState1ShouldCallFizz() {
    _objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup

    foo.Verify(x => x.Fizz());
}
[Test]
public void DoStuffToPushIntoState2ShouldntCallFizz() {
{
    objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
    foo.Verify(x => x.Fizz(), Times.Never());
}

Ответ 8

Следующий подход работает отлично для меня (используя Moq.Sequence)

    public void Justification()
    {
        var foo = new Mock<IFoo>(MockBehavior.Loose);
        foo.Setup(x => x.Fizz());

        var objectUnderTest = new ObjectUnderTest(foo.Object);

        objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup

        foo.Verify(x => x.Fizz());

        // Some cool stuff

        using (Sequence.Create())
        {
            foo.Setup(x => x.Fizz()).InSequence(Times.Never())
            objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
        }
    }

Сообщите мне, если это сработает для вас