Как проверить, что метод был вызван ровно один раз с помощью Moq? Вещь Verify()
vs. Verifable()
действительно запутанна.
Как проверить, что метод был вызван ровно один раз с помощью Moq?
Ответ 1
Вы можете использовать Times.Once()
или Times.Exactly(1)
:
mockContext.Verify(x => x.SaveChanges(), Times.Once());
mockContext.Verify(x => x.SaveChanges(), Times.Exactly(1));
Вот методы класса Times:
-
AtLeast
- указывает, что метод издевательства должен быть вызван как минимум раз. -
AtLeastOnce
- указывает, что метод издевательства должен вызываться как минимум как минимум. -
AtMost
- Указывает, что метод mocked должен быть вызван как раз как максимальное время. -
AtMostOnce
- указывает, что метод mocked должен вызываться один раз как максимум. -
Between
- указывает, что метод mocked должен быть вызван между и от времени. -
Exactly
- указывает, что метод издевательства должен вызываться точно раз. -
Never
- Указывает, что метод mocked не должен вызываться. -
Once
- указывает, что метод издевательства должен вызываться ровно один раз.
Просто помните, что это вызовы методов; Я продолжал спотыкаться, думая, что они были свойствами и забывают круглые скобки.
Ответ 2
Представьте, что мы строим калькулятор с одним методом для добавления 2 целых чисел. Давайте представим далее, что при вызове метода add он вызывает метод print один раз. Вот как мы можем это проверить:
public interface IPrinter
{
void Print(int answer);
}
public class ConsolePrinter : IPrinter
{
public void Print(int answer)
{
Console.WriteLine("The answer is {0}.", answer);
}
}
public class Calculator
{
private IPrinter printer;
public Calculator(IPrinter printer)
{
this.printer = printer;
}
public void Add(int num1, int num2)
{
printer.Print(num1 + num2);
}
}
А вот собственно тест с комментариями в коде для дальнейшего разъяснения:
[TestClass]
public class CalculatorTests
{
[TestMethod]
public void WhenAddIsCalled__ItShouldCallPrint()
{
/* Arrange */
var iPrinterMock = new Mock<IPrinter>();
// Let mock the method so when it is called, we handle it
iPrinterMock.Setup(x => x.Print(It.IsAny<int>()));
// Create the calculator and pass the mocked printer to it
var calculator = new Calculator(iPrinterMock.Object);
/* Act */
calculator.Add(1, 1);
/* Assert */
// Let make sure that the calculator Add method called printer.Print. Here we are making sure it is called once but this is optional
iPrinterMock.Verify(x => x.Print(It.IsAny<int>()), Times.Once);
// Or we can be more specific and ensure that Print was called with the correct parameter.
iPrinterMock.Verify(x => x.Print(3), Times.Once);
}
}
Примечание. По умолчанию Moq заглушает все свойства и методы, как только вы создаете объект Mock. Поэтому, даже не вызывая Setup
, Moq уже заглушил методы для IPrinter
, поэтому вы можете просто вызвать Verify
. Однако, как хорошая практика, я всегда настраиваю его, потому что нам может потребоваться принудительно применить параметры к методу, чтобы соответствовать определенным ожиданиям, или возвращаемое значение из метода, чтобы соответствовать определенным ожиданиям или количеству его вызовов.
Ответ 3
Контрольный контроллер может быть:
public HttpResponseMessage DeleteCars(HttpRequestMessage request, int id)
{
Car item = _service.Get(id);
if (item == null)
{
return request.CreateResponse(HttpStatusCode.NotFound);
}
_service.Remove(id);
return request.CreateResponse(HttpStatusCode.OK);
}
И когда метод DeleteCars вызывается с допустимым идентификатором, мы можем проверить, что метод Service remove, вызванный в этом тесте ровно один раз:
[TestMethod]
public void Delete_WhenInvokedWithValidId_ShouldBeCalledRevomeOnce()
{
//arange
const int carid = 10;
var car = new Car() { Id = carid, Year = 2001, Model = "TTT", Make = "CAR 1", Price=2000 };
mockCarService.Setup(x => x.Get(It.IsAny<int>())).Returns(car);
var httpRequestMessage = new HttpRequestMessage();
httpRequestMessage.Properties[HttpPropertyKeys.HttpConfigurationKey] = new HttpConfiguration();
//act
var result = carController.DeleteCar(httpRequestMessage, vechileId);
//assert
mockCarService.Verify(x => x.Remove(carid), Times.Exactly(1));
}