简体   繁体   English

使用Moq验证特定参数

[英]Verifying a specific parameter with Moq

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

I'm starting using Moq and struggling a bit. 我开始使用Moq并且有点挣扎。 I'm trying to verify that messageServiceClient is receiving the right parameter, which is an XmlElement, but I can't find any way to make it work. 我正在尝试验证messageServiceClient是否正在接收正确的参数,这是一个XmlElement,但我找不到任何方法使它工作。 It works only when I don't check a particular value. 它仅在我不检查特定值时才有效。

Any ideas? 有任何想法吗?

Partial answer: I've found a way to test that the xml sent to the proxy is correct, but I still don't think it's the right way to do it. 部分答案:我找到了一种方法来测试发送给代理的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());
}

By the way, how could I extract the expression from the Verify call? 那么,我如何从Verify调用中提取表达式?

If the verification logic is non-trivial, it will be messy to write a large lambda method (as your example shows). 如果验证逻辑不重要,那么编写一个大的lambda方法会很麻烦(如你的例子所示)。 You could put all the test statements in a separate method, but I don't like to do this because it disrupts the flow of reading the test code. 您可以将所有测试语句放在一个单独的方法中,但我不喜欢这样做,因为它会破坏读取测试代码的流程。

Another option is to use a callback on the Setup call to store the value that was passed into the mocked method, and then write standard Assert methods to validate it. 另一种选择是在Setup调用上使用回调来存储传递给mocked方法的值,然后编写标准的Assert方法来验证它。 For example: 例如:

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

I've been verifying calls in the same manner - I believe it is the right way to do it. 我一直在以相同的方式验证电话 - 我相信这是正确的方式。

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

If your lambda expression becomes unwieldy, you could create a function that takes MyObject as input and outputs true / false ... 如果你的lambda表达式变得难以处理,你可以创建一个以MyObject作为输入并输出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";
}

Also, be aware of a bug with Mock where the error message states that the method was called multiple times when it wasn't called at all. 另外,请注意Mock的一个错误,其中错误消息指出该方法在根本没有被调用时被多次调用。 They might have fixed it by now - but if you see that message you might consider verifying that the method was actually called. 他们现在可能已修复它 - 但如果您看到该消息,您可能会考虑验证该方法是否实际被调用。

EDIT: Here is an example of calling verify multiple times for those scenarios where you want to verify that you call a function for each object in a list (for example). 编辑:这是一个为您要验证为列表中的每个对象调用函数的情况多次调用验证的示例(例如)。

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

Same approach for setup... 同样的设置方法......

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

So each time GetStuff is called for that itemId, it will return stuff specific to that item. 所以每次为itemId调用GetStuff时,它都会返回特定于该项的东西。 Alternatively, you could use a function that takes itemId as input and returns stuff. 或者,您可以使用将itemId作为输入并返回内容的函数。

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

One other method I saw on a blog some time back (Phil Haack perhaps?) had setup returning from some kind of dequeue object - each time the function was called it would pull an item from a queue. 我在博客上看到的另一种方法(Phil Haack也许?)设置了从某种出队对象返回 - 每次调用该函数时它会从队列中拉出一个项目。

A simpler way would be to do: 一种更简单的方法是:

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

I believe that the problem in the fact that Moq will check for equality. 我相信Moq将检查平等的问题。 And, since XmlElement does not override Equals, it's implementation will check for reference equality. 并且,由于XmlElement不会覆盖Equals,因此它的实现将检查引用相等性。

Can't you use a custom object, so you can override equals? 你不能使用自定义对象,所以你可以覆盖等于?

Had one of these as well, but the parameter of the action was an interface with no public properties. 还有其中一个,但动作的参数是没有公共属性的接口。 Ended up using It.Is() with a seperate method and within this method had to do some mocking of the interface 使用It.Is()使用单独的方法结束,并且在此方法中必须对接口进行一些模拟

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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM