Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking Method Execution Times and Sequence

Tags:

c#

.net

moq

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?
like image 973
kspearrin Avatar asked May 30 '15 22:05

kspearrin


People also ask

What is a mock execution?

A mock execution is a stratagem in which a victim is deliberately but falsely made to feel that their execution or that of another person is imminent or is taking place. The subject is made to believe that they are being led to their own execution.

What is sequential mocking in Java?

Sequential mocking allows you to return different values on the same or different consecutive calls to one and the same type. In other words, you can set up expectations for successive calls of the same type.

What is the purpose of the Method Mock()?

Or the sequence of calls to an object. Moreover, it checks the communications among classes. We can use the method mock () for mocking purposes. Subsequently, let us see the implementation for testing of mock objects via the Mockito framework.

How do I change the behavior of a method in Moq?

When you’re mocking a method that’s called multiple times, you may want to change the behavior of the method each time it’s called. The way you do this with Moq is by using SetupSequence (), like this: mock.SetupSequence (t => t.ShouldRetry ()).Returns (true).Returns (true).Returns (false); Code language: C# (cs)


2 Answers

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();
like image 100
forsvarir Avatar answered Sep 21 '22 09:09

forsvarir


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");
    });
like image 30
Daniel Slater Avatar answered Sep 19 '22 09:09

Daniel Slater