Wie überprüfe ich, ob eine Methode mit Moq genau einmal aufgerufen wurde?

111

Wie überprüfe ich, ob eine Methode mit Moq genau einmal aufgerufen wurde? Das Verify()vs. Verifable()Ding ist wirklich verwirrend.

Josh Kodroff
quelle

Antworten:

164

Sie können verwenden Times.Once(), oder Times.Exactly(1):

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

Hier sind die Methoden für die Times- Klasse:

  • AtLeast - Gibt an, dass eine verspottete Methode mindestens mal aufgerufen werden soll.
  • AtLeastOnce - Gibt an, dass eine verspottete Methode mindestens einmal aufgerufen werden soll.
  • AtMost - Gibt an, dass eine verspottete Methode maximal mal aufgerufen werden soll.
  • AtMostOnce - Gibt an, dass eine verspottete Methode maximal einmal aufgerufen werden soll.
  • Between - Gibt an, dass eine verspottete Methode zwischen von und bis zu Zeiten aufgerufen werden soll.
  • Exactly - Gibt an, dass eine verspottete Methode genau mal aufgerufen werden soll.
  • Never - Gibt an, dass eine verspottete Methode nicht aufgerufen werden soll.
  • Once - Gibt an, dass eine verspottete Methode genau einmal aufgerufen werden soll.

Denken Sie daran, dass es sich um Methodenaufrufe handelt. Ich stolperte immer wieder, dachte, sie seien Eigenschaften und vergaß die Klammern.

Jeff Ogata
quelle
2
Wie erhält / richtet man den mockContext ein?
Choco
2
@Choco Ich nehme an, das ist nur seine Mock-Instanz. Es war also so etwas wie var mockContext = new Mock<IContext>()das einzurichten.
Zack Huber
Ich frage mich nur , wie AtLeast, AtMost, Betweenoder Exactlyals Objekt angesehen werden könnten. Ich meine, sie brauchen einen Parameter, um etwas zu tun.
Danylo Yelizarov
8

Stellen Sie sich vor, wir bauen einen Taschenrechner mit einer Methode zum Hinzufügen von 2 ganzen Zahlen. Stellen wir uns weiter vor, die Anforderung besteht darin, dass beim Aufrufen der add-Methode die print-Methode einmal aufgerufen wird. So würden wir das testen:

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

Und hier ist der eigentliche Test mit Kommentaren im Code zur weiteren Verdeutlichung:

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

Hinweis : Standardmäßig stoppt Moq alle Eigenschaften und Methoden, sobald Sie ein Mock-Objekt erstellen. Auch ohne Aufruf Setuphat Moq die Methoden bereits blockiert, IPrintersodass Sie einfach aufrufen können Verify. Als bewährte Methode habe ich sie jedoch immer eingerichtet, da wir möglicherweise die Parameter für die Methode erzwingen müssen, um bestimmte Erwartungen zu erfüllen, oder den Rückgabewert der Methode, um bestimmte Erwartungen zu erfüllen, oder die Häufigkeit, mit der sie aufgerufen wurde.

Codierung von Yoshi
quelle
Ich rief an Verify, Times.Onceohne jemals anzurufen Setup. Ich würde sicherlich erwarten Verify, in diesem Fall in die Luft zu jagen, aber das tat es nicht.
dudeNumber4
@ dudeNumber4 Nein, es wird nicht gesprengt, da Moq standardmäßig alle Eigenschaften und Methoden stubbt, sobald Sie ein MockObjekt erstellen . Auch ohne Aufruf Setuphat Moq die Methoden bereits blockiert, IPrintersodass Sie einfach aufrufen können Verify. Als gute Vorgehensweise habe ich es jedoch immer eingerichtet, da wir möglicherweise die Parameter für die Methode oder den Rückgabewert der Methode erzwingen müssen.
CodingYoshi
Entschuldigung, das war eine schreckliche Erklärung. Ich habe angerufen Times.Exactly(1)und es ist nicht fehlgeschlagen, als die Methode tatsächlich zweimal aufgerufen wurde. Erst nach dem Hinzufügen Setupfür die betreffende Methode ist sie korrekt fehlgeschlagen.
dudeNumber4
2

Testcontroller kann sein:

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

Und wenn die DeleteCars-Methode mit einer gültigen ID aufgerufen wird, können wir überprüfen, ob die Dienstentfernungsmethode bei diesem Test genau einmal aufgerufen wurde:

 [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));
    }
Sanjeev Bhusal
quelle