Come posso verificare che un metodo sia stato chiamato esattamente una volta con Moq?


112

Come posso verificare che un metodo sia stato chiamato esattamente una volta con Moq? La cosa Verify()contro Verifable()è davvero confusa.

Risposte:


165

Puoi usare Times.Once(), o Times.Exactly(1):

mockContext.Verify(x => x.SaveChanges(), Times.Once());
mockContext.Verify(x => x.SaveChanges(), Times.Exactly(1));

Ecco i metodi della classe Times :

  • AtLeast - Specifica che un metodo deriso deve essere invocato volte il minimo.
  • AtLeastOnce - Specifica che un metodo deriso deve essere richiamato almeno una volta.
  • AtMost - Specifica che un metodo deriso deve essere invocato per il tempo massimo.
  • AtMostOnce - Specifica che un metodo deriso deve essere invocato una volta al massimo.
  • Between - Specifica che un metodo deriso deve essere invocato tra le ore from e to.
  • Exactly - Specifica che un metodo deriso deve essere invocato esattamente volte volte.
  • Never - Specifica che un metodo deriso non deve essere richiamato.
  • Once - Specifica che un metodo deriso deve essere invocato esattamente una volta.

Ricorda solo che sono chiamate a metodi; Continuavo a inciampare, pensando che fossero proprietà e dimenticando le parentesi.


2
quindi come si ottiene / imposta il mockContext?
Choco

2
@ Choco presumo che sia solo la sua istanza Mock. Quindi è stato qualcosa di simile var mockContext = new Mock<IContext>()a configurarlo.
Zack Huber

Mi chiedo solo come AtLeast, AtMost, Between, o Exactlypotrebbe essere visto come proprietà. Voglio dire, hanno ovviamente bisogno di un parametro per fare qualcosa.
Danylo Yelizarov

8

Immagina di costruire una calcolatrice con un metodo per aggiungere 2 numeri interi. Immaginiamo ulteriormente che il requisito sia che quando viene chiamato il metodo add, esso chiama il metodo print una volta. Ecco come lo testeremo:

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);
    }
}

Ed ecco il test vero e proprio con commenti all'interno del codice per ulteriori chiarimenti:

[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 : per impostazione predefinita Moq bloccherà tutte le proprietà e i metodi non appena crei un oggetto Mock. Quindi, anche senza chiamare Setup, Moq ha già bloccato i metodi per IPrinterquindi puoi semplicemente chiamare Verify. Tuttavia, come buona pratica, l'ho sempre impostato perché potrebbe essere necessario applicare i parametri al metodo per soddisfare determinate aspettative, o il valore restituito dal metodo per soddisfare determinate aspettative o il numero di volte che è stato chiamato.


Stavo chiamando Verify, Times.Oncesenza mai chiamare Setup. Mi sarei sicuramente aspettato Verifydi esplodere in quel caso, ma non è stato così.
dudeNumber4

@ dudeNumber4 No, non esploderà perché per impostazione predefinita Moq stub tutte le proprietà e i metodi non appena crei un Mockoggetto. Quindi, anche senza chiamare Setup, Moq ha già bloccato i metodi per IPrinterquindi puoi semplicemente chiamare Verify. Tuttavia, come buona pratica, l'ho sempre impostato perché potrebbe essere necessario applicare i parametri al metodo o il valore restituito dal metodo.
CodingYoshi

Scusa, era una spiegazione terribile. Ho chiamato Times.Exactly(1)e non ha fallito quando il metodo è stato effettivamente chiamato due volte. Solo dopo aver aggiunto Setupper il metodo in questione non è riuscito correttamente.
dudeNumber4

2

Il controller del test può essere:

  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 il metodo DeleteCars viene chiamato con un id valido, possiamo verificarlo, il metodo di rimozione del servizio chiamato esattamente una volta da questo test:

 [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));
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.