2011-02-10 69 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) 
    ) 
);