256
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. 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. 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.

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?

Vitaliy Ulantikov
  • 10,157
  • 3
  • 61
  • 54
Luis Mirabal
  • 2,606
  • 2
  • 16
  • 12

5 Answers5

356

If the verification logic is non-trivial, it will be messy to write a large lambda method (as your example shows). 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. 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));
Keith K
  • 2,893
  • 4
  • 33
  • 43
Rich Tebb
  • 6,806
  • 2
  • 23
  • 25
  • 13
    One large benefit to this approach is that it will give you specific test failures for how the object incorrect (as you're testing each individually). – Rob Church Apr 22 '13 at 08:13
  • 2
    I thought I was the only one who did this, glad to see it's a reasonable approach! – Will Appleby Nov 25 '15 at 16:09
  • 3
    I think using It.Is(validator) as per Mayo is better as it avoids the slightly awkward way of saving the parameter value as part of the lambda – stevec Sep 29 '17 at 15:04
  • 2
    is this thread safe, for example when running tests in parallel? – Anton Tolken Jul 22 '19 at 02:49
  • 1
    @AntonTolken I haven't tried it, but in my example, the object that is updated is a local variable (saveObject) so it should be thread safe. – Rich Tebb Jul 23 '19 at 12:01
  • @stevec but this approach only guarantees you that a specific call was made, but not if has been called in any other way. – royalTS Sep 15 '20 at 06:40
201

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...

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. 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. Alternatively, you could use a function that takes itemId as input and returns stuff.

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.

manman
  • 4,743
  • 3
  • 30
  • 42
Mayo
  • 10,544
  • 6
  • 45
  • 90
  • 1
    Thanks, it makes sense to me. What I still can't understand is when to specify details in Setup or Verify. It's quite confusing. At the moment, I'm just allowing anything in Setup and specifying the values in Verify. – Luis Mirabal Feb 10 '11 at 15:10
  • How do you think I can check the messages when there are multiple calls? The client take messages and can create multiple queueableMessages, which will end up in multiple calls and in each of those calls, I have to check different messages. I'm still struggling with unit testing in general, I'm not very familiar with it yet. – Luis Mirabal Feb 10 '11 at 15:21
  • I don't think there's a magic silver bullet in terms of how you should do this. It takes practice and you start getting better. For me, I only specify parameters when I have something to compare them to and when I'm not already testing that parameter in another test. As for multiple calls there are several approaches. For setting up and verifying a function that is called multiple times, I usually call setup or verify (Times.Once()) for each call that I expect - often with a for loop. You can use the specific parameters to isolate each call. – Mayo Feb 10 '11 at 16:38
  • I added some examples for multiple calls - see answer above. – Mayo Feb 10 '11 at 16:47
  • Thanks for your reply about multiple calls, I'll take that approach. What do you think about checking conditions like the answer below? Using asserts seems more natural to me than the lambda expressions in Verify calls, but at the same time a bit tricky. – Luis Mirabal Feb 11 '11 at 09:32
  • 1
    "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. They might have fixed it by now - but if you see that message you might consider verifying that the method was actually called." - A bug like this completely invalidates a mocking library IMHO. How ironic they didn't have proper testing code for it :-) – Gianluca Ghettini Jan 21 '16 at 10:53
28

A simpler way would be to do:

ObjectA.Verify(
    a => a.Execute(
        It.Is<Params>(p => p.Id == 7)
    )
);
dmitry.sergeyev
  • 439
  • 5
  • 11
  • I can't seem to get this working, I'm trying to verify that my method was called with the Code.WRCC as a parameter. But my test always passes, even though the parameter passed is WRDD.. `m.Setup(x => x.CreateSR(Code.WRDD)).ReturnsAsync(0); await ClassUnderTest.CompleteCC(accountKey, It.IsAny(), mockRequest); m.Verify(x => x.CreateSR(It.Is(p => p == Code.WRCC)), Times.Once());` – Greg Quinn Oct 07 '19 at 23:39
  • @Greg Quinn doublecheck return type of your TestMethod, it should be `async Task`, not `void`. – Andrii Viazovskyi Mar 02 '21 at 10:09
1

I believe that the problem in the fact that Moq will check for equality. And, since XmlElement does not override Equals, it's implementation will check for reference equality.

Can't you use a custom object, so you can override equals?

Fernando
  • 4,029
  • 1
  • 26
  • 35
  • Yes, I ended up doing that. I realised that the problem was checking the Xml. In the second part of the question I added a possible answer deserialising the xml to an object – Luis Mirabal Feb 11 '11 at 09:44
1

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

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
}
ds4940
  • 3,382
  • 1
  • 13
  • 20