Printshop
Printshop

Reputation: 33

How to get relevant error message when method called with unexpected parameter?

In Moq, I want to assert that a method TestMethod is called with a specific parameter. When the test fails, I want to see a useful error message similar to:

TestMethod called with unexpected value X for parameter P where Y expected.

public interface ITestObject
{
    void TestMethod(int parameter);
}

Just for sake of illustration, I could achieve this using a handcoded mock as follows:

Assert.AreEqual failed. Expected:<3>. Actual:<2>. TestMethod called with unexpected value for parameter 'actual'.

public class MockTestMethodParameter : ITestObject
{
    private readonly int expected;
    public MockTestMethodParameter(int expected) { this.expected = expected; }
    
    public void TestMethod(int actual)
    {
        Assert.AreEqual(actual, expected, $"{nameof(TestMethod)} called with unexpected value for parameter '{nameof(actual)}'.");
    }
}

[TestMethod]
public void TestHandcodedMockFailure()
{
    var mock = new MockTestMethodParameter(expected: 2);            
    mock.TestMethod(actual: 3);
}

My problem is, I can't figure out how to do this in Moq. When I set up my Mock and call Verify(), I get the following unexpected and unclear error message, instead of the message I was hoping for:

Expected invocation on the mock at least once, but was never performed: test => test.TestMethod(It.Is(2, GenericEqualityComparer))

Performed invocations:

MockUnitTest1.ITestObject:1 (test):

UnitTest1.ITestObject.TestMethod(3)

[TestMethod]
public void TestMoqFailure()
{
  var expected = 2;
  var actual = 3;

  var mock = new Moq.Mock<ITestObject>(Moq.MockBehavior.Strict);
  mock.Setup(test => test.TestMethod(Moq.It.IsAny<int>())).Verifiable();
  mock.Object.TestMethod(actual);
  mock.Verify(test => test.TestMethod(Moq.It.Is(expected, EqualityComparer<int>.Default)));
}

Granted, the information is there, but I expected something more along the lines of, "TestMethod was invoked, but with incorrect parameters." It confuses me when Moq reports that TestMethod was not invoked because, intuitively, I did invoke the mock. I called TestMethod with It.IsAny(), as declared in the mock setup.

I've tried many different adjustments, but none yielding the desired result:

Is this simply the way Moq reports error results for Verify()? I'm new to Moq, and while this behavior is unexpected to me, perhaps it is working as designed.

Upvotes: 1

Views: 535

Answers (1)

Printshop
Printshop

Reputation: 33

This appears to be normal behavior for mock frameworks in general. Doing the same thing with Telerik JustMock yields a similar error message:

Occurrence expectation failed. Expected exactly 1 call. Calls so far: 0

[TestMethod]
public void TestJustMockFailure()
{
    var expected = 2;
    var actual = 3;

    var mock = Mock.Create<ITestObject>(Behavior.Strict);
    Mock.Arrange(() => mock.TestMethod(Arg.IsAny<int>()));
    mock.TestMethod(actual);
    Mock.Assert(() => mock.TestMethod(Arg.Is(expected)), Occurs.Once());
}

In summary:

The OP's use case (my use case) is valid, verifying a particular parameter value was passed to a mocked method. However, the error message does not report the parameter mismatch as such. Rather, it reports that the entire mocked method call did not occur. This is by design, and appears to be common among popular mock frameworks.

Upvotes: 1

Related Questions