2011-02-10 28 views
100
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à gặp khó khăn một chút. Tôi đang cố gắng xác minh rằng messageServiceClient đang nhận đúng tham số, là một XmlElement, 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ể.Xác minh thông số cụ thể bằng Moq

Bất kỳ ý tưởng nào?

Câu trả lời một phần: Tôi đã tìm thấy cách để kiểm tra xem xml được gửi tới proxy có đúng không, nhưng tôi vẫn không nghĩ đó là cách phù hợp để thực hiện.

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?

Trả lời

149

Nếu logic xác minh là không nhỏ, 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 thử nghiệm trong một phương thức riêng biệt, nhưng tôi không thích làm điều này vì nó phá vỡ luồng đọc mã kiểm tra.

Một tùy chọn khác là sử dụng gọi lại trên cuộc gọi Thiết lập để lưu trữ giá trị được chuyển vào phương thức giả, sau đó viết các phương thức chuẩn Assert để xác thực.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)); 
+5

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 kiểm tra cụ thể về cách đối tượng không chính xác (như bạn đang thử nghiệm từng cá nhân). –

+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ý! –

+0

Tôi nghĩ rằng việc sử dụng It.Is (validator) theo Mayo là tốt hơn vì nó tránh được cách hơi lúng túng khi lưu giá trị tham số như một phần của lambda – stevec

48

Tôi đã xác minh cuộc gọi theo cách tương tự - Tôi tin rằng đó là cách phù hợp để 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 mang theo MyObject như đầu vào và đầu ra đúng/sai ...

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, cần chú ý một lỗi với Mock nơi 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 thức này thực sự được gọi.

EDIT: Dưới đây là một ví dụ về gọi xác minh nhiều lần cho những kịch bản mà bạn muốn để xác minh rằng bạn gọi một chức năng cho từng đối tượng trong một 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ùng cách tiếp cận để 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 là cho rằng ItemID, nó sẽ trở lại những thứ cụ thể để mục đó. Ngoài ra, bạn có thể sử dụng một hàm nhận itemId làm đầu vào và trả về nội dung.

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

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

+0

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

+0

Bạn nghĩ sao tôi có thể kiểm tra tin nhắn khi có nhiều cuộc gọi? Khách hàng nhận tin nhắn và có thể tạo nhiều hàng đợiMessages, 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ó. –

+0

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ó thực hành và bạn bắt đầu nhận được tốt hơn. Đối với tôi, tôi chỉ xác định các thông số khi tôi có thứ gì đó để so sánh chúng với và khi tôi chưa thử nghiệm 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 hàm đượ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 vòng lặp for. Bạn có thể sử dụng các thông số cụ thể để tách biệt từng cuộc gọi. – Mayo

1

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

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

+0

Vâng, tôi đã kết thúc làm điều đó. Tôi nhận ra rằng vấn đề đang 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ể deserialising xml vào một đối tượng –

8

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

ObjectA.Verify(
    a => a.Execute(
     It.Is<Params>(p => p.Id == 7) 
    ) 
); 
Các vấn đề liên quan