6

I am using Moq paired with an Interface of methods. I need to test that the methods in this interface are executed in a certain sequence as well as a certain number of times for each one.

Interface

public interface IInterface
{
    void MethodOne(string foo);
    void MethodTwo(string foo);
}

Method

// MyClass stuff ...

public async Task Run()
{
    MethodOne("foo");
    MethodTwo("foo");
}

// ...

Tests

I've written this test to verify that the methods are only executed a certain amount of times (once):

[TestMethod]
public async Task Test()
{
    var mock = new Mock<IInterface>();
    var mockSequence = new MockSequence();

    var obj = new MyClass();
    await obj.Run();

    mock.Verify(i=> i.MethodOne("foo"), Times.Once());
    mock.Verify(i=> i.MethodTwo("foo"), Times.Once());
}

This works fine...

I've tried these tests for determining a certain sequence is properly met, but the test seems to always pass.

[TestMethod]
public async Task Test()
{
    var mock = new Mock<IInterface>();
    var mockSequence = new MockSequence();

    var obj = new MyClass();
    await obj.Run();

    mock.InSequence(mockSequence).Setup(i => i.MethodOne("foo"));
    mock.InSequence(mockSequence).Setup(i => i.MethodTwo("foo"));
}

Should pass, and does...

[TestMethod]
public async Task Test()
{
    var mock = new Mock<IInterface>();
    var mockSequence = new MockSequence();

    var obj = new MyClass();
    await obj.Run();

    mock.InSequence(mockSequence).Setup(i => i.MethodTwo("foo")); // swapped order here
    mock.InSequence(mockSequence).Setup(i => i.MethodOne("foo"));
}

Should not pass, but does...

  1. What do I need to do differently to verify proper sequence is met?
  2. How do I combine the two so that I can test number of execution times AND proper sequence?
kspearrin
  • 10,238
  • 9
  • 53
  • 82
  • I marked this as a duplicate because the two questions seem to refer to the same issue. If they don't, let me know and I'll vote to reopen. – dcastro May 30 '15 at 22:28
  • I read that question prior to asking this one. That question was resolved with an answer that said this was a bug in Moq. That was 3 years ago... I assume it's not a bug still. – kspearrin May 30 '15 at 22:31
  • i've reopened the question – dcastro May 30 '15 at 22:34

2 Answers2

11

Your example tests for InSequence seem to be in the wrong order. You should be performing the Setup, before you invoke the Run method. I've assumed this is a typo and that your actual tests do the Setup, then invoke the object in the correct order. It's also not clear how your mock of IInterface gets to MyClass. In my final example, I've assumed it's actually injected...

Moq's InSequence support only seems to work if you're using strict mocks. If you create your Mock like this:

var mock = new Mock<IInterface>(MockBehavior.Strict);

Then this will work:

mock.InSequence(mockSequence).Setup(i => i.MethodOne("foo"));
mock.InSequence(mockSequence).Setup(i => i.MethodTwo("foo"));

while this fails:

mock.InSequence(mockSequence).Setup(i => i.MethodTwo("foo")); // swapped order here
mock.InSequence(mockSequence).Setup(i => i.MethodOne("foo"));

Note, the error that comes is about the method missing a corresponding setup... rather than it being called out of sequence, which isn't necessarily the easiest to decode if you're not expecting it.

The alternate approach, if you don't want to / can't use strict mocks is to implement your own sequence checking using callbacks. Something like this:

int sequence = 1;

mock.Setup(i => i.MethodOne("foo"))
    .Callback(()=>Assert.AreEqual(1, sequence++, "Method called out of sequence"));
mock.Setup(i => i.MethodTwo("foo"))
    .Callback(() => Assert.AreEqual(2, sequence++, "Method called out of sequence"));

var obj = new MyClass(mock.Object);
await obj.Run();
forsvarir
  • 10,749
  • 6
  • 46
  • 77
4

This may be further off topic than you want to go but NSubstitute is a great mocking library that handles this very well. In NSubstitute it's just:

  var mock = Substitute.For<IInterface>();
  var obj = new MyClass();
  await obj.Run();

  Received.InOrder(() => {
    mock.MethodOne("foo");
    mock.MethodTwo("foo");
    });
Daniel Slater
  • 4,123
  • 4
  • 28
  • 39