2011-02-10 71 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(); 
} 

我開始使用Moq並掙扎了一下。 我想驗證messageServiceClient正在接收正確的參數,這是一個XmlElement,但我找不到任何方法使其工作。它只有在我沒有檢查特定值時纔有效。使用Moq驗證特定參數

任何想法?

部分回答: 我發現了一種方法來測試發送到代理的xml是否正確,但我仍然認爲這不是正確的方法。

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

順便說一句,我怎麼能從驗證呼叫中提取表達式?

回答

149

如果驗證邏輯不平凡,寫一個大的lambda方法會很麻煩(如你的例子所示)。你可以把所有的測試語句放在一個單獨的方法中,但我不喜歡這樣做,因爲它破壞了閱讀測試代碼的流程。

另一種方法是在Setup調用中使用回調來存儲傳遞到模擬方法中的值,然後編寫標準Assert方法來驗證它。例如:

// 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)); 
48

我一直在以同樣的方式驗證呼叫 - 我相信這是正確的方式來做到這一點。

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

如果您的lambda表達式變得很困難,你可以創建一個函數,它爲MyObject作爲輸入和輸出真/假...

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

此外,要知道用模擬的一個錯誤的地方錯誤消息指出,當方法完全未被調用時,該方法被多次調用。他們現在可能已經修復了這個問題 - 但是如果你看到這個信息,你可能會考慮驗證這個方法實際上是被調用的。

編輯:這是一個調用驗證多次的例子,您需要驗證您爲列表中的每個對象(例如)調用函數的場景。

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

的設置相同的方法......

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

因此,每個GetStuff稱爲該的itemId的時候,它會返回特定於該項目的東西。或者,您可以使用一個將itemId作爲輸入並返回內容的函數。我在博客上看到了一段時間回來

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

另外一個方法(?菲爾哈克也許)不得不設置從某種離隊對象的返回 - 每個函數被調用它會從隊列中拉出一個項目的時間。

+0

謝謝,這對我來說很有意義。 我仍然無法理解的是何時在安裝或驗證中指定詳細信息。這很混亂。目前,我只允許安裝程序中的任何內容並在驗證中指定值。 – 2011-02-10 15:10:28

+0

您認爲我有多次通話時可以查看留言嗎? 客戶端接收消息,並可以創建多個隊列消息,這將最終在多個調用中,並在每個這些調用中,我必須檢查不同的消息。 我仍然在單元測試中苦苦掙扎,我對它還不是很熟悉。 – 2011-02-10 15:21:35

+0

我不認爲你應該如何做到這一點,有一個神奇的銀彈。這需要練習,你開始變得更好。對於我來說,只有當我有一些東西可以與另一個測試中的參數進行比較時才指定參數。至於多次呼叫,有幾種方法。爲了設置和驗證多次調用的函數,我通常爲每個我期望的調用調用setup或verify(Times.Once()) - 通常使用for循環。您可以使用特定參數來隔離每個呼叫。 – Mayo 2011-02-10 16:38:24

1

我相信Moq會檢查平等的事實。而且,由於XmlElement不會覆蓋Equals,因此它的實現將檢查引用是否相等。

難道你不能使用自定義對象,所以你可以覆蓋等於?

8

更簡單的方法是做:

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