Using Moq to verify calls are made in the correct order
Asked Answered
K

11

81

I need to test the following method:

CreateOutput(IWriter writer)
{
    writer.Write(type);
    writer.Write(id);
    writer.Write(sender);

    // many more Write()s...
}

I've created a Moq'd IWriter and I want to ensure that the Write() methods are called in the right order.

I have the following test code:

var mockWriter = new Mock<IWriter>(MockBehavior.Strict);
var sequence = new MockSequence();
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedType));
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedId));
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedSender));

However, the second call to Write() in CreateOutput() (to write the id value) throws a MockException with the message "IWriter.Write() invocation failed with mock behavior Strict. All invocations on the mock must have a corresponding setup.".

I'm also finding it hard to find any definitive, up-to-date documentation/examples of Moq sequences.

Am I doing something wrong, or can I not set up a sequence using the same method? If not, is there an alternative I can use (preferably using Moq/NUnit)?

Kropp answered 15/5, 2012 at 13:53 Comment(3)
possible duplicate of How to test method call order with MoqFlag
The latest release of Moq, v4.2 has "improved mock invocation sequence testing" according to its release notes.Kropp
I am using a v.4.2.x and can confirm that the sequence functionality is working for me.Chairborne
P
86

There is bug when using MockSequence on same mock. It definitely will be fixed in later releases of Moq library (you can also fix it manually by changing Moq.MethodCall.Matches implementation).

If you want to use Moq only, then you can verify method call order via callbacks:

int callOrder = 0;
writerMock.Setup(x => x.Write(expectedType)).Callback(() => Assert.That(callOrder++, Is.EqualTo(0)));
writerMock.Setup(x => x.Write(expectedId)).Callback(() => Assert.That(callOrder++, Is.EqualTo(1)));
writerMock.Setup(x => x.Write(expectedSender)).Callback(() => Assert.That(callOrder++, Is.EqualTo(2)));
Pneumonic answered 15/5, 2012 at 22:8 Comment(4)
Thanks for the information, I've resorted to using this method. Hope there will be later releases of Moq! One of the best testing tools there is...Kropp
One word of warning here, if the Write() method is never invoked, none of these callbacks will have a chance to assert anything. Be sure to add a catch all verification that the method is invoked at least once.Margit
There is ongoing discussion on github regarding sequencing, for anyone who wants to contribute / comment: github.com/moq/moq4/issues/75Profant
This is a great idea, thanks! I actually prefer this, as it means I can overwrite the setups I had before. I did add .Verifiable to each setup, and a .Verify() on the end, to check that they were actually run, as well as being run in the right order :)Artful
K
14

I've managed to get the behaviour I want, but it requires downloading a 3rd-party library from http://dpwhelan.com/blog/software-development/moq-sequences/

The sequence can then be tested using the following:

var mockWriter = new Mock<IWriter>(MockBehavior.Strict);
using (Sequence.Create())
{
    mockWriter.Setup(x => x.Write(expectedType)).InSequence();
    mockWriter.Setup(x => x.Write(expectedId)).InSequence();
    mockWriter.Setup(x => x.Write(expectedSender)).InSequence();
}

I've added this as an answer partly to help document this solution, but I'm still interested in whether something similar could be achieved using Moq 4.0 alone.

I'm not sure if Moq is still in development, but fixing the problem with the MockSequence, or including the moq-sequences extension in Moq would be good to see.

Kropp answered 15/5, 2012 at 14:33 Comment(1)
shame you can't pm. Cool didn't know that existed :D. But you are really stubbing btw once you get into this territory which maybe why moq is a bit lacking in this area - and yeah the project seems very quiet. Looked at its issues and sequencing has been on there for 4 years outstanding. I also noticed the latest release does have final in its name.Pyrophotometer
E
13

I wrote an extension method that will assert based on order of invocation.

public static class MockExtensions
{
  public static void ExpectsInOrder<T>(this Mock<T> mock, params Expression<Action<T>>[] expressions) where T : class
  {
    // All closures have the same instance of sharedCallCount
    var sharedCallCount = 0;
    for (var i = 0; i < expressions.Length; i++)
    {
      // Each closure has it's own instance of expectedCallCount
      var expectedCallCount = i;
      mock.Setup(expressions[i]).Callback(
        () =>
          {
            Assert.AreEqual(expectedCallCount, sharedCallCount);
            sharedCallCount++;
          });
    }
  }
}

It works by taking advantage of the way that closures work with respect to scoped variables. Since there is only one declaration for sharedCallCount, all of the closures will have a reference to the same variable. With expectedCallCount, a new instance is instantiated each iteration of the loop (as opposed to simply using i in the closure). This way, each closure has a copy of i scoped only to itself to compare with the sharedCallCount when the expressions are invoked.

Here's a small unit test for the extension. Note that this method is called in your setup section, not your assertion section.

[TestFixture]
public class MockExtensionsTest
{
  [TestCase]
  {
    // Setup
    var mock = new Mock<IAmAnInterface>();
    mock.ExpectsInOrder(
      x => x.MyMethod("1"),
      x => x.MyMethod("2"));

    // Fake the object being called in order
    mock.Object.MyMethod("1");
    mock.Object.MyMethod("2");
  }

  [TestCase]
  {
    // Setup
    var mock = new Mock<IAmAnInterface>();
    mock.ExpectsInOrder(
      x => x.MyMethod("1"),
      x => x.MyMethod("2"));

    // Fake the object being called out of order
    Assert.Throws<AssertionException>(() => mock.Object.MyMethod("2"));
  }
}

public interface IAmAnInterface
{
  void MyMethod(string param);
}
Expiratory answered 31/10, 2013 at 16:11 Comment(0)
H
7

Recently, I put together two features for Moq: VerifyInSequence() and VerifyNotInSequence(). They work even with Loose Mocks. However, these are only available in a moq repository fork:

https://github.com/grzesiek-galezowski/moq4

and await more comments and testing before deciding on whether they can be included in official moq releaase. However, nothing prevents you from downloading the source as ZIP, building it into a dll and giving it a try. Using these features, the sequence verification you need could be written as such:

var mockWriter = new Mock<IWriter>() { CallSequence = new LooseSequence() };

//perform the necessary calls

mockWriter.VerifyInSequence(x => x.Write(expectedType));
mockWriter.VerifyInSequence(x => x.Write(expectedId));
mockWriter.VerifyInSequence(x => x.Write(expectedSender));

(note that you can use two other sequences, depending on your needs. Loose sequence will allow any calls between the ones you want to verify. StrictSequence will not allow this and StrictAnytimeSequence is like StrictSequence (no method calls between verified calls), but allows the sequence to be preceeded by any number of arbitrary calls.

If you decide to give this experimental feature a try, please comment with your thoughts on: https://github.com/Moq/moq4/issues/21

Thanks!

Hilariohilarious answered 29/12, 2012 at 16:59 Comment(0)
C
7

The simplest solution would be using a Queue:

var expectedParameters = new Queue<string>(new[]{expectedType,expectedId,expectedSender});
mockWriter.Setup(x => x.Write(expectedType))
          .Callback((string s) => Assert.AreEqual(expectedParameters.Dequeue(), s));
Crystlecs answered 25/9, 2016 at 23:25 Comment(0)
S
7

I've just had a similar scenario, and inspired by the accepted answer, I've used the following approach:

//arrange
var someServiceToTest = new SomeService();

var expectedCallOrder = new List<string>
{
    "WriteA",
    "WriteB",
    "WriteC"
};
var actualCallOrder = new List<string>();

var mockWriter = new Mock<IWriter>();
mockWriter.Setup(x => x.Write("A")).Callback(() => { actualCallOrder.Add("WriteA"); });
mockWriter.Setup(x => x.Write("B")).Callback(() => { actualCallOrder.Add("WriteB"); });
mockWriter.Setup(x => x.Write("C")).Callback(() => { actualCallOrder.Add("WriteC"); });

//act
someServiceToTest.CreateOutput(_mockWriter.Object);

//assert
Assert.AreEqual(expectedCallOrder, actualCallOrder);
Sternpost answered 31/12, 2020 at 15:20 Comment(0)
H
5

Moq has a little-known feature called Capture.In, which can capture arguments passed to a method. With it, you can verify call order like this:

var calls = new List<string>();
var mockWriter = new Mock<IWriter>();
mockWriter.Setup(x => x.Write(Capture.In(calls)));

CollectionAssert.AreEqual(calls, expectedCalls);

If you have overloads with different types, you can run the same setup for overloads too.

Harleyharli answered 28/1, 2021 at 13:33 Comment(0)
M
1

My scenario was methods without parameters:

public interface IWriter
    {
    void WriteA ();
    void WriteB ();
    void WriteC ();
    }

So I used Invocations property on the Mock to compare what was called:

var writer = new Mock<IWriter> ();

new SUT (writer.Object).Run ();

Assert.Equal (
    writer.Invocations.Select (invocation => invocation.Method.Name),
    new[]
        {
        nameof (IWriter.WriteB),
        nameof (IWriter.WriteA),
        nameof (IWriter.WriteC),
        });

You could also append the invocation.Arguments to check method calls with parameters.

Also the failure message is more clear than just expected 1 but was 5:

    expected
["WriteB", "WriteA", "WriteC"]
    but was
["WriteA", "WriteB"]
Melainemelamed answered 29/5, 2020 at 5:18 Comment(0)
P
0

I suspect that expectedId is not what you expect.

However i'd probably just write my own implementation of IWriter to verify in this case ... probably a lot easier (and easier to change later).

Sorry for no Moq advice directly. I love it, but haven't done this in it.

do you maybe need to add .Verify() at the end of each setup? (That really is a guess though i'm afraid).

Pyrophotometer answered 15/5, 2012 at 14:3 Comment(1)
I've double-checked and the values are correct and as expected. Rolling my own mocks is something I try to avoid now I use Moq, but may have to resort to! And Verify() cannot be added after the setup, only Verifiable() - which sadly makes no difference. Thanks for the pointers though.Kropp
W
0

I am late to this party but I wanted to share a solution that worked for me since it seems as though all of the referenced solutions did not work with verifying the same method call (with the same arguments) multiple times in order. In addition the referenced bug, Moq Issue #478 was closed without a solution.

The solution presented utilizes the MockObject.Invocations list to determine order and sameness.

public static void VerifyInvocations<T>(this Mock<T> mock, params Expression<Action<T>>[] expressions) where T : class
{
    Assert.AreEqual(mock.Invocations.Count, expressions.Length,
        $"Number of invocations did not match expected expressions! Actual invocations: {Environment.NewLine}" +
        $"{string.Join(Environment.NewLine, mock.Invocations.Select(i => i.Method.Name))}");

    for (int c = 0; c < mock.Invocations.Count; c++)
    {
        IInvocation expected = mock.Invocations[c];
        MethodCallExpression actual = expressions[c].Body as MethodCallExpression;

        // Verify that the same methods were invoked
        Assert.AreEqual(expected.Method, actual.Method, $"Did not invoke the expected method at call {c + 1}!");

        // Verify that the method was invoked with the correct arguments
        CollectionAssert.AreEqual(expected.Arguments.ToList(),
            actual.Arguments
                .Select(arg =>
                {
                    // Expressions treat the Argument property as an Expression, do this to invoke the getter and get the actual value.
                    UnaryExpression objectMember = Expression.Convert(arg, typeof(object));
                    Expression<Func<object>> getterLambda = Expression.Lambda<Func<object>>(objectMember);
                    Func<object> objectValueGetter = getterLambda.Compile();
                    return objectValueGetter();
                })
                .ToList(),
            $"Did not invoke step {c + 1} method '{expected.Method.Name}' with the correct arguments! ");
    }
}
Wernher answered 28/9, 2020 at 14:2 Comment(0)
P
0

In my case some functionality of a mocked dependency makes sense to be called only after another one. Therefore I setup the mock like this:

_serviceMock
    .Setup(f => f.PrimaryMethod(It.Is<Any>()))
    .Callback(() =>
    {
        // Following properties are available only after PrimaryMethod call
        _serviceMock.Setup(f => f.DependentPropertyOne).Returns(_result1);
        _serviceMock.Setup(f => f.DependentPropertyTwo).Returns(_result2);
    });

This approach is nice and tidy, allows for cleaner tests code, and tests depending less on the details of implementation. I believe it should be used when possible, but it cannot cover as much as explicit invocation order checking.

Pinprick answered 7/3 at 13:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.