Xác minh một tham số cụ thể với Moq


168
public void SubmitMessagesToQueue_OneMessage_SubmitSuccessfully()
{
    var messageServiceClientMock = new Mock<IMessageServiceClient>();
    var queueableMessage = CreateSingleQueueableMessage();
    var message = queueableMessage[0];
    var xml = QueueableMessageAsXml(queueableMessage);
    messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(xml)).Verifiable();
    //messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(It.IsAny<XmlElement>())).Verifiable();

    var serviceProxyFactoryStub = new Mock<IMessageServiceClientFactory>();
    serviceProxyFactoryStub.Setup(proxyFactory => proxyFactory.CreateProxy()).Returns(essageServiceClientMock.Object);
    var loggerStub = new Mock<ILogger>();

    var client = new MessageClient(serviceProxyFactoryStub.Object, loggerStub.Object);
    client.SubmitMessagesToQueue(new List<IMessageRequestDTO> {message});

    //messageServiceClientMock.Verify(proxy => proxy.SubmitMessage(xml), Times.Once());
    messageServiceClientMock.Verify();
}

Tôi đang bắt đầu sử dụng Moq và đấu tranh một chút. Tôi đang cố gắng xác minh rằng messageServiceClient đang nhận đúng tham số, đó là XmlE bổ sung, nhưng tôi không thể tìm thấy bất kỳ cách nào để làm cho nó hoạt động. Nó chỉ hoạt động khi tôi không kiểm tra một giá trị cụ thể.

Có ý kiến ​​gì không?

Câu trả lời một phần: Tôi đã tìm thấy một cách để kiểm tra rằng xml được gửi tới proxy là chính xác, nhưng tôi vẫn không nghĩ rằng đó là cách đúng đắn để làm điều đó.

public void SubmitMessagesToQueue_OneMessage_SubmitSuccessfully()
{
    var messageServiceClientMock = new Mock<IMessageServiceClient>();
    messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(It.IsAny<XmlElement>())).Verifiable();
    var serviceProxyFactoryStub = new Mock<IMessageServiceClientFactory>();
    serviceProxyFactoryStub.Setup(proxyFactory => proxyFactory.CreateProxy()).Returns(messageServiceClientMock.Object);
    var loggerStub = new Mock<ILogger>();

    var client = new MessageClient(serviceProxyFactoryStub.Object, loggerStub.Object);
    var message = CreateMessage();
    client.SubmitMessagesToQueue(new List<IMessageRequestDTO> {message});

    messageServiceClientMock.Verify(proxy => proxy.SubmitMessage(It.Is<XmlElement>(xmlElement => XMLDeserializer<QueueableMessage>.Deserialize(xmlElement).Messages.Contains(message))), Times.Once());
}

Nhân tiện, làm cách nào tôi có thể trích xuất biểu thức từ cuộc gọi Xác minh?

Câu trả lời:


250

Nếu logic xác minh là không tầm thường, sẽ rất lộn xộn khi viết một phương thức lambda lớn (như ví dụ của bạn cho thấy). Bạn có thể đặt tất cả các câu lệnh kiểm tra theo một phương thức riêng biệt, nhưng tôi không muốn làm điều này vì nó làm gián đoạn dòng đọc mã kiểm tra.

Một tùy chọn khác là sử dụng một cuộc gọi lại trong lệnh gọi Thiết lập để lưu trữ giá trị được truyền vào phương thức giả định, sau đó viết các Assertphương thức chuẩn để xác thực nó. Ví dụ:

// Arrange
MyObject saveObject;
mock.Setup(c => c.Method(It.IsAny<int>(), It.IsAny<MyObject>()))
        .Callback<int, MyObject>((i, obj) => saveObject = obj)
        .Returns("xyzzy");

// Act
// ...

// Assert
// Verify Method was called once only
mock.Verify(c => c.Method(It.IsAny<int>(), It.IsAny<MyObject>()), Times.Once());
// Assert about saveObject
Assert.That(saveObject.TheProperty, Is.EqualTo(2));

6
Một lợi ích lớn cho phương pháp này là nó sẽ cung cấp cho bạn các lỗi thử nghiệm cụ thể về cách đối tượng không chính xác (như bạn đang thử nghiệm từng cái riêng lẻ).
Rob Church

1
Tôi nghĩ rằng tôi là người duy nhất đã làm điều này, vui mừng khi thấy đó là một cách tiếp cận hợp lý!
Will Appleby

3
Tôi nghĩ rằng sử dụng It.Is <MyObject> (trình xác nhận) theo Mayo sẽ tốt hơn vì nó tránh được cách hơi khó xử khi lưu giá trị tham số như một phần của lambda
stevec

chủ đề này có an toàn không, ví dụ khi chạy thử nghiệm song song?
Anton Tolken

@AntonTolken Tôi chưa thử nó, nhưng trong ví dụ của tôi, đối tượng được cập nhật là một biến cục bộ (saveObject) vì vậy nó phải là luồng an toàn.
Rich Tebb

111

Tôi đã xác minh các cuộc gọi theo cách tương tự - Tôi tin rằng đó là cách đúng đắn để thực hiện.

mockSomething.Verify(ms => ms.Method(
    It.IsAny<int>(), 
    It.Is<MyObject>(mo => mo.Id == 5 && mo.description == "test")
  ), Times.Once());

Nếu biểu thức lambda của bạn trở nên khó sử dụng, bạn có thể tạo một hàm lấy MyObjectđầu vào và đầu ra true/ false...

mockSomething.Verify(ms => ms.Method(
    It.IsAny<int>(), 
    It.Is<MyObject>(mo => MyObjectFunc(mo))
  ), Times.Once());

private bool MyObjectFunc(MyObject myObject)
{
  return myObject.Id == 5 && myObject.description == "test";
}

Ngoài ra, hãy lưu ý đến một lỗi với Mock trong đó thông báo lỗi cho biết phương thức được gọi nhiều lần khi nó không được gọi. Bây giờ họ có thể đã sửa nó - nhưng nếu bạn thấy thông báo đó, bạn có thể xem xét xác minh rằng phương thức đó thực sự được gọi.

EDIT: Dưới đây là một ví dụ về việc gọi xác minh nhiều lần cho các tình huống mà bạn muốn xác minh rằng bạn gọi một hàm cho từng đối tượng trong danh sách (ví dụ).

foreach (var item in myList)
  mockRepository.Verify(mr => mr.Update(
    It.Is<MyObject>(i => i.Id == item.Id && i.LastUpdated == item.LastUpdated),
    Times.Once());

Cách tiếp cận tương tự để thiết lập ...

foreach (var item in myList) {
  var stuff = ... // some result specific to the item
  this.mockRepository
    .Setup(mr => mr.GetStuff(item.itemId))
    .Returns(stuff);
}

Vì vậy, mỗi lần GetStuff được gọi cho itemId đó, nó sẽ trả về những thứ cụ thể cho mục đó. Ngoài ra, bạn có thể sử dụng một hàm lấy itemId làm đầu vào và trả về công cụ.

this.mockRepository
    .Setup(mr => mr.GetStuff(It.IsAny<int>()))
    .Returns((int id) => SomeFunctionThatReturnsStuff(id));

Một phương pháp khác mà tôi thấy trên blog một thời gian trước đây (có lẽ Phil Haack?) Đã thiết lập trở về từ một loại đối tượng dequeue - mỗi lần hàm được gọi là nó sẽ kéo một mục từ hàng đợi.


1
Cảm ơn, nó có ý nghĩa với tôi. Điều tôi vẫn không thể hiểu là khi nào chỉ định chi tiết trong Cài đặt hoặc Xác minh. Nó khá khó hiểu. Hiện tại, tôi chỉ cho phép mọi thứ trong Cài đặt và chỉ định các giá trị trong Xác minh.
Luis Mirabal

Bạn nghĩ tôi có thể kiểm tra tin nhắn như thế nào khi có nhiều cuộc gọi? Máy khách nhận tin nhắn và có thể tạo nhiều hàng đợi, điều này sẽ kết thúc trong nhiều cuộc gọi và trong mỗi cuộc gọi đó, tôi phải kiểm tra các tin nhắn khác nhau. Tôi vẫn đang vật lộn với thử nghiệm đơn vị nói chung, tôi chưa quen lắm với nó.
Luis Mirabal

Tôi không nghĩ rằng có một viên đạn bạc ma thuật về cách bạn nên làm điều này. Nó cần thực hành và bạn bắt đầu trở nên tốt hơn. Đối với tôi, tôi chỉ chỉ định các tham số khi tôi có thứ gì đó để so sánh chúng và khi tôi chưa kiểm tra tham số đó trong một thử nghiệm khác. Đối với nhiều cuộc gọi, có một số cách tiếp cận. Để thiết lập và xác minh một chức năng được gọi nhiều lần, tôi thường gọi thiết lập hoặc xác minh (Times.Once ()) cho mỗi cuộc gọi mà tôi mong đợi - thường với một vòng lặp for. Bạn có thể sử dụng các tham số cụ thể để cách ly từng cuộc gọi.
Mayo

Tôi đã thêm một số ví dụ cho nhiều cuộc gọi - xem câu trả lời ở trên.
Mayo

1
"Ngoài ra, hãy chú ý đến một lỗi với Mock trong đó thông báo lỗi nói rằng phương thức đó được gọi nhiều lần khi nó không được gọi. Họ có thể đã sửa nó ngay bây giờ - nhưng nếu bạn thấy thông báo đó, bạn có thể xem xét xác minh rằng phương pháp đã thực sự được gọi. " - Một lỗi như thế này làm mất hiệu lực hoàn toàn một thư viện chế giễu IMHO. Thật mỉa mai khi họ không có mã kiểm tra thích hợp cho nó :-)
Gianluca Ghettini

20

Một cách đơn giản hơn sẽ là:

ObjectA.Verify(
    a => a.Execute(
        It.Is<Params>(p => p.Id == 7)
    )
);

Tôi dường như không thể làm việc này được, tôi đang cố xác minh rằng phương thức của tôi được gọi với Code.WRCC làm tham số. Nhưng bài kiểm tra của tôi luôn vượt qua, mặc dù tham số đã vượt qua là WRDĐ .. m.Setup(x => x.CreateSR(Code.WRDD)).ReturnsAsync(0); await ClassUnderTest.CompleteCC(accountKey, It.IsAny<int>(), mockRequest); m.Verify(x => x.CreateSR(It.Is<Code>(p => p == Code.WRCC)), Times.Once());
Greg Quinn

1

Tôi tin rằng vấn đề trong thực tế là Moq sẽ kiểm tra sự bình đẳng. Và, vì XmlEuity không ghi đè Bằng, nên việc triển khai sẽ kiểm tra sự bằng nhau tham chiếu.

Bạn không thể sử dụng một đối tượng tùy chỉnh, vì vậy bạn có thể ghi đè bằng?


Vâng, tôi đã kết thúc việc đó. Tôi nhận ra rằng vấn đề là kiểm tra Xml. Trong phần thứ hai của câu hỏi, tôi đã thêm một câu trả lời có thể giải thích xml cho một đối tượng
Luis Mirabal

1

Cũng có một trong số này, nhưng tham số của hành động là một giao diện không có thuộc tính công khai. Đã kết thúc bằng cách sử dụng It.Is () với một phương thức riêng biệt và trong phương thức này đã phải thực hiện một số chế nhạo giao diện

public interface IQuery
{
    IQuery SetSomeFields(string info);
}

void DoSomeQuerying(Action<IQuery> queryThing);

mockedObject.Setup(m => m.DoSomeQuerying(It.Is<Action<IQuery>>(q => MyCheckingMethod(q)));

private bool MyCheckingMethod(Action<IQuery> queryAction)
{
    var mockQuery = new Mock<IQuery>();
    mockQuery.Setup(m => m.SetSomeFields(It.Is<string>(s => s.MeetsSomeCondition())
    queryAction.Invoke(mockQuery.Object);
    mockQuery.Verify(m => m.SetSomeFields(It.Is<string>(s => s.MeetsSomeCondition(), Times.Once)
    return true
}
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.