Como posso verificar se um método foi chamado exatamente uma vez com o Moq? A coisa Verify()
vs. Verifable()
é realmente confusa.
Como posso verificar se um método foi chamado exatamente uma vez com o Moq? A coisa Verify()
vs. Verifable()
é realmente confusa.
Respostas:
Você pode usar Times.Once()
, ou Times.Exactly(1)
:
mockContext.Verify(x => x.SaveChanges(), Times.Once());
mockContext.Verify(x => x.SaveChanges(), Times.Exactly(1));
Aqui estão os métodos da classe Times :
AtLeast
- Especifica que um método simulado deve ser invocado vezes, no mínimo.AtLeastOnce
- Especifica que um método simulado deve ser chamado pelo menos uma vez.AtMost
- Especifica que um método simulado deve ser invocado vezes no máximo.AtMostOnce
- Especifica que um método simulado deve ser chamado uma vez, no máximo.Between
- Especifica que um método simulado deve ser chamado entre os horários de e para.Exactly
- Especifica que um método simulado deve ser chamado exatamente vezes.Never
- Especifica que um método simulado não deve ser chamado.Once
- Especifica que um método simulado deve ser chamado exatamente uma vez.Lembre-se de que são chamadas de método; Eu ficava tropeçando, pensando que eram propriedades e esquecendo os parênteses.
var mockContext = new Mock<IContext>()
com isso.
AtLeast
, AtMost
, Between
, ou Exactly
poderia ser visto como propriedade. Quer dizer, eles obviamente precisam de um parâmetro para fazer algo.
Imagine que estamos construindo uma calculadora com um método para adicionar 2 inteiros. Vamos imaginar ainda que o requisito é que, quando o método add é chamado, ele chama o método print uma vez. Aqui está como testaríamos isso:
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);
}
}
E aqui está o teste real com comentários dentro do código para maiores esclarecimentos:
[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 : Por padrão, o Moq fará o stub de todas as propriedades e métodos assim que você criar um objeto Mock. Portanto, mesmo sem chamar Setup
, o Moq já criou os métodos de para IPrinter
que você possa apenas chamar Verify
. No entanto, como uma boa prática, sempre o configuro porque podemos precisar impor os parâmetros do método para atender a certas expectativas, ou o valor de retorno do método para atender a certas expectativas ou o número de vezes que ele foi chamado.
Verify
, Times.Once
sem nunca ligar Setup
. Eu certamente esperaria Verify
explodir nesse caso, mas isso não aconteceu.
Mock
objeto. Portanto, mesmo sem chamar Setup
, o Moq já criou os métodos de para IPrinter
que você possa apenas chamar Verify
. No entanto, como uma boa prática, eu sempre o configuro porque pode ser necessário impor os parâmetros ao método ou o valor de retorno do método.
Times.Exactly(1)
e não falhou quando o método foi de fato chamado duas vezes. Somente após adicionar Setup
para o método em questão, ele falhou corretamente.
O controlador de teste pode 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);
}
E quando o método DeleteCars é chamado com um id válido, podemos verificar se o método Service remove chamado exatamente uma vez por este teste:
[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));
}