如何使用 Moq 验证某个方法被调用了一次?

发布于 2024-10-02 23:28:36 字数 89 浏览 1 评论 0原文

如何使用 Moq 验证某个方法被调用了一次? Verify()Verifable() 的事情确实令人困惑。

How do I verify a method was called exactly once with Moq? The Verify() vs. Verifable() thing is really confusing.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

伊面 2024-10-09 23:28:36

您可以使用 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 - 指定应在 from 和 to 时间之间调用模拟方法。
  • Exactly - 指定模拟方法应该被精确调用几次。
  • Never - 指定不应调用模拟方法。
  • Once - 指定模拟方法应该被调用一次。

只要记住它们是方法调用即可;我总是被绊倒,以为它们是属性,却忘记了括号。

You can use Times.Once(), or Times.Exactly(1):

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

Here are the methods on the Times class:

  • AtLeast - Specifies that a mocked method should be invoked times times as minimum.
  • AtLeastOnce - Specifies that a mocked method should be invoked one time as minimum.
  • AtMost - Specifies that a mocked method should be invoked times time as maximum.
  • AtMostOnce - Specifies that a mocked method should be invoked one time as maximum.
  • Between - Specifies that a mocked method should be invoked between from and to times.
  • Exactly - Specifies that a mocked method should be invoked exactly times times.
  • Never - Specifies that a mocked method should not be invoked.
  • Once - Specifies that a mocked method should be invoked exactly one time.

Just remember that they are method calls; I kept getting tripped up, thinking they were properties and forgetting the parentheses.

归属感 2024-10-09 23:28:36

想象一下,我们正在构建一个计算器,其中包含一种将 2 个整数相加的方法。我们进一步想象一下需求是,当调用add方法时,调用一次print方法。以下是我们测试的方法:

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 对象后立即存根所有属性和方法。因此,即使不调用 Setup,Moq 也已经存根了 IPrinter 的方法,因此您只需调用 Verify 即可。然而,作为一个好的实践,我总是设置它,因为我们可能需要强制方法的参数以满足某些期望,或者强制方法的返回值以满足某些期望或调用它的次数。

Imagine we are building a calculator with one method for adding 2 integers. Let's further imagine the requirement is that when the add method is called, it calls the print method once. Here is how we would test this:

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

And here is the actual test with comments within the code for further clarification:

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

Note: By default Moq will stub all the properties and methods as soon as you create a Mock object. So even without calling Setup, Moq has already stubbed the methods for IPrinter so you can just call Verify. However, as a good practice, I always set it up because we may need to enforce the parameters to the method to meet certain expectations, or the return value from the method to meet certain expectations or the number of times it has been called.

七秒鱼° 2024-10-09 23:28:36

测试控制器可能是:

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

并且当使用有效的 id 调用 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));
    }

Test controller may be :

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

And When DeleteCars method called with valid id, then we can verify that, Service remove method called exactly once by this 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));
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文