Comment vérifier qu'une méthode a été appelée exactement une fois avec Moq?

112

Comment vérifier qu'une méthode a été appelée exactement une fois avec Moq? La chose Verify()contre Verifable()est vraiment déroutante.

Josh Kodroff
la source

Réponses:

165

Vous pouvez utiliser Times.Once(), ou Times.Exactly(1):

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

Voici les méthodes de la classe Times :

  • AtLeast - Spécifie qu'une méthode simulée doit être invoquée au minimum.
  • AtLeastOnce - Spécifie qu'une méthode simulée doit être appelée une fois au minimum.
  • AtMost - Spécifie qu'une méthode simulée doit être invoquée au maximum.
  • AtMostOnce - Spécifie qu'une méthode simulée doit être invoquée une fois au maximum.
  • Between - Spécifie qu'une méthode simulée doit être invoquée entre les heures de et à.
  • Exactly - Spécifie qu'une méthode simulée doit être invoquée exactement fois fois.
  • Never - Spécifie qu'une méthode simulée ne doit pas être appelée.
  • Once - Spécifie qu'une méthode simulée doit être appelée exactement une fois.

N'oubliez pas que ce sont des appels de méthode; Je n'arrêtais pas de trébucher, pensant que c'étaient des propriétés et oubliant les parenthèses.

Jeff Ogata
la source
2
alors comment obtenir / configurer le mockContext?
Choco
2
@Choco Je suppose que ce n'est que son exemple Mock. C'était donc quelque chose comme var mockContext = new Mock<IContext>()mettre en place cela.
Zack Huber
Je me demande comment AtLeast, AtMost, Betweenou Exactlypourrait être considéré comme la propriété. Je veux dire, ils ont évidemment besoin d'un paramètre pour faire quelque chose.
Danylo Yelizarov le
8

Imaginez que nous construisons une calculatrice avec une méthode pour ajouter 2 entiers. Imaginons en outre que l'exigence est que lorsque la méthode add est appelée, elle appelle la méthode print une fois. Voici comment nous testerions ceci:

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

Et voici le test réel avec des commentaires dans le code pour plus de précisions:

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

Remarque : Par défaut, Moq stub de toutes les propriétés et méthodes dès que vous créez un objet Mock. Donc, même sans appeler Setup, Moq a déjà stubé les méthodes pour IPrinterque vous puissiez simplement appeler Verify. Cependant, comme bonne pratique, je la configure toujours car nous pouvons avoir besoin d'appliquer les paramètres de la méthode pour répondre à certaines attentes, ou la valeur de retour de la méthode pour répondre à certaines attentes ou le nombre de fois qu'elle a été appelée.

CodageYoshi
la source
J'appelais Verify, Times.Oncesans jamais appeler Setup. Je m'attendrais certainement Verifyà exploser dans ce cas, mais ce n'est pas le cas.
dudeNumber4
@ dudeNumber4 Non, il n'explosera pas car, par défaut, Moq stub de toutes les propriétés et méthodes dès que vous créez un Mockobjet. Donc, même sans appeler Setup, Moq a déjà stubé les méthodes pour IPrinterque vous puissiez simplement appeler Verify. Cependant, comme bonne pratique, je le configure toujours car nous pouvons avoir besoin d'appliquer les paramètres à la méthode ou la valeur de retour de la méthode.
CodingYoshi
Désolé, c'était une terrible explication. J'ai appelé Times.Exactly(1)et cela n'a pas échoué lorsque la méthode a en fait été appelée deux fois. Ce n'est qu'après l'ajout Setupde la méthode en question qu'elle a échoué correctement.
dudeNumber4
2

Le contrôleur de test peut être:

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

Et lorsque la méthode DeleteCars est appelée avec un identifiant valide, nous pouvons le vérifier, la méthode de suppression du service appelée exactement une fois par ce 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));
    }
sanjeev bhusal
la source