¿Cómo verifico que se haya llamado a un método exactamente una vez con Moq? La cosa Verify()
vs. Verifable()
es realmente confusa.
¿Cómo verifico que se haya llamado a un método exactamente una vez con Moq? La cosa Verify()
vs. Verifable()
es realmente confusa.
Respuestas:
Puede usar Times.Once()
, o Times.Exactly(1)
:
mockContext.Verify(x => x.SaveChanges(), Times.Once());
mockContext.Verify(x => x.SaveChanges(), Times.Exactly(1));
Estos son los métodos de la clase Times :
AtLeast
- Especifica que un método simulado debe invocarse veces como mínimo.AtLeastOnce
- Especifica que un método simulado debe invocarse una vez como mínimo.AtMost
- Especifica que un método simulado debe invocarse por tiempo como máximo.AtMostOnce
- Especifica que un método simulado debe invocarse una vez como máximo.Between
- Especifica que se debe invocar un método simulado entre las horas desde y hasta.Exactly
- Especifica que un método simulado debe invocarse exactamente las veces.Never
- Especifica que no se debe invocar un método simulado.Once
- Especifica que un método simulado debe invocarse exactamente una vez.Recuerde que son llamadas a métodos; Seguí tropezando, pensando que eran propiedades y olvidándome del paréntesis.
var mockContext = new Mock<IContext>()
configurar eso.
AtLeast
, AtMost
, Between
, o Exactly
podría ser visto como una propiedad. Quiero decir, obviamente necesitan un parámetro para hacer algo.
Imagina que estamos construyendo una calculadora con un método para sumar 2 enteros. Imaginemos además que el requisito es que cuando se llama al método add, llama al método print una vez. Así es como probaríamos esto:
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);
}
}
Y aquí está la prueba real con comentarios dentro del código para mayor aclaración:
[TestClass]
public class CalculatorTests
{
[TestMethod]
public void WhenAddIsCalled__ItShouldCallPrint()
{
/* Arrange */
var iPrinterMock = new Mock<IPrinter>();
// Let's 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's make sure that the calculator's 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);
}
}
Nota : De forma predeterminada, Moq eliminará todas las propiedades y métodos tan pronto como cree un objeto Mock. Entonces, incluso sin llamar Setup
, Moq ya ha eliminado los métodos para IPrinter
que pueda llamar Verify
. Sin embargo, como buena práctica, siempre lo configuro porque es posible que necesitemos hacer cumplir los parámetros del método para cumplir con ciertas expectativas, o el valor de retorno del método para cumplir con ciertas expectativas o la cantidad de veces que se ha llamado.
Verify
, Times.Once
sin llamar nunca Setup
. Ciertamente esperaría Verify
explotar en ese caso, pero no fue así.
Mock
objeto. Entonces, incluso sin llamar Setup
, Moq ya ha eliminado los métodos para IPrinter
que pueda llamar Verify
. Sin embargo, como buena práctica, siempre lo configuro porque es posible que necesitemos aplicar los parámetros al método o el valor de retorno del método.
Times.Exactly(1)
y no falló cuando el método se llamó dos veces. Solo después de agregar Setup
el método en cuestión, falló correctamente.
El controlador de prueba puede ser:
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);
}
Y cuando se llama al método DeleteCars con una identificación válida, entonces podemos verificar que, el método de eliminación del servicio se llama exactamente una vez por esta prueba:
[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));
}