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 - все, что я могу сделать, кроме проверочных вызовов.
Ответы
Ответ 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
}
}
Сообщите мне, если это сработает для вас