Làm cách nào để xác minh một phương thức được gọi chính xác một lần với Moq?


112

Làm cách nào để xác minh một phương thức được gọi chính xác một lần với Moq? Điều Verify()so với Verifable()thực sự là khó hiểu.

Câu trả lời:


165

Bạn có thể sử dụng Times.Once(), hoặc Times.Exactly(1):

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

Đây là các phương thức trên lớp Times :

  • AtLeast - Chỉ định rằng một phương thức giả lập phải được gọi số lần là tối thiểu.
  • AtLeastOnce - Chỉ định rằng một phương thức giả lập nên được gọi một lần là tối thiểu.
  • AtMost - Chỉ định rằng một phương thức giả lập nên được gọi với thời gian tối đa.
  • AtMostOnce - Chỉ định rằng một phương thức giả lập nên được gọi tối đa một lần.
  • Between - Chỉ định rằng một phương thức chế nhạo nên được gọi từ và thường.
  • Exactly - Chỉ định rằng một phương thức giả lập nên được gọi chính xác lần.
  • Never - Chỉ định rằng không được gọi một phương thức giả mạo.
  • Once - Chỉ định rằng một phương thức giả lập phải được gọi chính xác một lần.

Chỉ cần nhớ rằng chúng là các cuộc gọi phương thức; Tôi tiếp tục bị vấp ngã, nghĩ rằng chúng là tài sản và quên dấu ngoặc đơn.


2
vậy làm cách nào để bạn tải / thiết lập mockContext?
Choco

2
@Choco Tôi cho rằng đó chỉ là ví dụ Mock của anh ấy. Vì vậy, nó giống như một cái gì đó var mockContext = new Mock<IContext>()để thiết lập điều đó.
Zack Huber

Tôi chỉ tự hỏi như thế nào AtLeast, AtMost, Between, hoặc Exactlycó thể được xem như tài sản. Ý tôi là, họ cần một tham số để làm điều gì đó.
Danylo Yelizarov

8

Hãy tưởng tượng chúng ta đang xây dựng một máy tính với một phương pháp để cộng 2 số nguyên. Hãy hình dung thêm yêu cầu là khi phương thức add được gọi, nó sẽ gọi phương thức print một lần. Đây là cách chúng tôi sẽ kiểm tra điều này:

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

Và đây là thử nghiệm thực tế với nhận xét trong mã để làm rõ thêm:

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

Lưu ý : Theo mặc định, Moq sẽ khai báo tất cả các thuộc tính và phương thức ngay khi bạn tạo một đối tượng Mock. Vì vậy, ngay cả khi không gọi Setup, Moq đã cung cấp các phương thức IPrinterđể bạn có thể gọi Verify. Tuy nhiên, như một phương pháp hay, tôi luôn thiết lập nó vì chúng ta có thể cần phải thực thi các tham số cho phương thức để đáp ứng những mong đợi nhất định hoặc giá trị trả về từ phương thức để đáp ứng những mong đợi nhất định hoặc số lần nó đã được gọi.


Tôi đang gọi Verify, Times.Oncemà không bao giờ gọi Setup. Tôi chắc chắn sẽ Verifynổ tung trong trường hợp đó, nhưng nó đã không.
dudeNumber 4

@ dudeNumber4 Không, nó sẽ không nổ tung vì mặc định Moq sẽ khai báo tất cả các thuộc tính và phương thức ngay khi bạn tạo một Mockđối tượng. Vì vậy, ngay cả khi không gọi Setup, Moq đã cung cấp các phương thức IPrinterđể bạn có thể gọi Verify. Tuy nhiên, như một thông lệ tốt, tôi luôn thiết lập nó vì chúng ta có thể cần phải thực thi các tham số cho phương thức hoặc giá trị trả về từ phương thức.
CodingYoshi

Xin lỗi, đó là một lời giải thích tồi tệ. Tôi đã gọi Times.Exactly(1)và nó không thất bại khi phương thức thực tế được gọi hai lần. Chỉ sau khi thêm Setupphương thức được đề cập, nó mới không thành công.
dudeNumber 4

2

Bộ điều khiển kiểm tra có thể là:

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

Và khi phương thức DeleteCars được gọi với id hợp lệ, thì chúng tôi có thể xác minh rằng, phương thức loại bỏ Dịch vụ được gọi chính xác một lần bằng bài kiểm tra này:

 [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));
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.