Как проверить, что метод был вызван ровно один раз с помощью Moq?

112

Как проверить, что метод был вызван ровно один раз с помощью Moq? Verify()Против Verifable()вещь действительно заблуждение.

Джош Кодрофф
источник

Ответы:

165

Вы можете использовать Times.Once(), или Times.Exactly(1):

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

Вот методы класса Times :

  • AtLeast - Указывает, что фиктивный метод должен вызываться как минимум раз.
  • AtLeastOnce - Указывает, что фиктивный метод должен вызываться как минимум один раз.
  • AtMost - Указывает, что фиктивный метод должен вызываться максимальное время.
  • AtMostOnce - Указывает, что фиктивный метод должен быть вызван максимум один раз.
  • Between - Указывает, что фиктивный метод должен вызываться между от и до времени.
  • Exactly - Указывает, что фиктивный метод должен вызываться ровно раз.
  • Never - Указывает, что фиктивный метод не должен вызываться.
  • Once - Указывает, что фиктивный метод должен быть вызван ровно один раз.

Просто помните, что это вызовы методов; Я все время сбивался с толку, думая, что это свойства, и забывая о скобках.

Джефф Огата
источник
2
так как же получить / настроить mockContext?
Choco
2
@Choco, я полагаю, это просто его Mock. Так что это было что-то вроде того, var mockContext = new Mock<IContext>()чтобы это настроить.
Zack Huber
Я просто удивляюсь , как AtLeast, AtMost, Betweenили Exactlyможно было бы рассматривать как собственность. Я имею в виду, что им, очевидно, нужен параметр, чтобы что-то делать.
Даниил Елизаров
8

Представьте, что мы создаем калькулятор с одним методом сложения двух целых чисел. Давайте представим себе, что при вызове метода добавления он вызывает метод печати один раз. Вот как мы это протестируем:

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

А вот собственно тест с комментариями в коде для уточнения:

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

Примечание . По умолчанию Moq заглушит все свойства и методы, как только вы создадите объект Mock. Таким образом, даже без вызова SetupMoq уже заглушил методы, IPrinterпоэтому вы можете просто позвонить Verify. Однако в качестве хорошей практики я всегда настраиваю его, потому что нам может потребоваться принудительно применить параметры метода, чтобы удовлетворить определенные ожидания, или возвращаемое значение метода, чтобы удовлетворить определенные ожидания, или количество раз, когда он был вызван.

Кодирование: Йоши
источник
Я звонил Verify, Times.Onceно не звонил Setup. Я, конечно, ожидал, Verifyчто в этом случае взорвется, но этого не произошло.
dudeNumber4
@ dudeNumber4 Нет, он не взорвется, потому что по умолчанию Moq заглушит все свойства и методы, как только вы создадите Mockобъект. Таким образом, даже без вызова SetupMoq уже заглушил методы, IPrinterпоэтому вы можете просто позвонить Verify. Однако в качестве хорошей практики я всегда настраиваю его, потому что нам может потребоваться принудительно применить параметры к методу или возвращаемое значение из метода.
CodingYoshi
Извините, это было ужасное объяснение. Я позвонил, Times.Exactly(1)и он не потерпел неудачу, когда метод фактически был вызван дважды. Только после добавления Setupрассматриваемого метода произошла ошибка.
dudeNumber4
2

Контроллер тестирования может быть:

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

И когда метод DeleteCars вызывается с действительным идентификатором, мы можем проверить это, метод удаления службы вызывается ровно один раз в этом тесте:

 [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));
    }
Санджив Бхусал
источник