Como posso verificar se um método foi chamado exatamente uma vez com o Moq?


112

Como posso verificar se um método foi chamado exatamente uma vez com o Moq? A coisa Verify()vs. Verifable()é realmente confusa.

Respostas:


165

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.


2
então, como você obtém / configura o mockContext?
— Choco

2
@Choco Presumo que seja apenas sua instância Mock. Então foi algo parecido var mockContext = new Mock<IContext>()com isso.
— Zack Huber

Eu só quero saber como AtLeast, AtMost, Between, ou Exactlypoderia ser visto como propriedade. Quer dizer, eles obviamente precisam de um parâmetro para fazer algo.
— Danylo Yelizarov

8

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 IPrinterque 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.


Eu estava ligando Verify, Times.Oncesem nunca ligar Setup. Eu certamente esperaria Verifyexplodir nesse caso, mas isso não aconteceu.
— dudeNumber4

@ dudeNumber4 Não, não explodirá porque, por padrão, o Moq fará o stub de todas as propriedades e métodos assim que você criar um Mockobjeto. Portanto, mesmo sem chamar Setup, o Moq já criou os métodos de para IPrinterque 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.
— CodingYoshi

Desculpe, essa foi uma explicação terrível. Liguei Times.Exactly(1)e não falhou quando o método foi de fato chamado duas vezes. Somente após adicionar Setuppara o método em questão, ele falhou corretamente.
— dudeNumber4

2

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));
    }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.