2

I use JustMock framework and have the following assertion:

Mock.Assert(() => activityListenerMock.PeriodPassed(
  Arg.Matches<Period>(e => e.Length == expectedLength)));

It fails with cryptic message:

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

How can I get better message. I want to know with what value it was called.

Method is actually called but with wrong argument because when I change assertion to following it passes:

Mock.Assert(() => activityListenerMock.PeriodPassed(
  Arg.IsAny<Period>()));
Krzysztof Kot
  • 648
  • 1
  • 10
  • 15
  • Can you show a short but complete example? It would make it easier to help you. – Jon Skeet Oct 03 '14 at 08:26
  • That message might be more helpful than you think. It means that that assertion is using an implicit `OccursAtLeast(1)`. Your test will fail if the asserted method is not executed as expected by the matcher. If you're debugging and you're seeing the method being executed, it's because the original method is being executed, not the mocked one. As you discovered yourself, it works when you fix the matcher to actually match the test scenario. – GuiRitter Jun 11 '21 at 16:04

4 Answers4

6

One way to see what argument was passed to PeriodPassed is to use JustMock's DebugView

Place DebugView.IsTraceEnabled = true; at the beginning of the test and add DebugView.CurrentState to the watch. Towards the end you will see something to the tune of this: Invocations: (ByRef ...).PeriodPassed("period value will go here") called 1 time; (signature: ...)

The period value will be shown in the Invocations list.

Another way to do this, is to extract the matcher into a separate lambda and use a breakpoint: Predicate<Period> matcher = e => e.Length == expectedLength; Mock.Assert(() => activityListenerMock.PeriodPassed( Arg.Matches<Period>(e => matcher(e))));

Now you can place a breakpoint inside the predicate and check the value of the e argument. This works because now the predicate is not an expression but an actual function, so now you can debug it.

Stefan Dragnev
  • 14,143
  • 6
  • 48
  • 52
  • Setting DebugView.IsTraceEnabled = true will cause the failure message to include the value of DebugView.CurrentState. Beware though that it slows down your tests considerably. – kevinpo Jan 18 '16 at 17:37
3

Just to what Stefan Dragnev wrote. I used his idea and then added logic to validate the input. If not the expected value called Assert.Fail(). Not sure if there is a better way, but this works:

Mock.Arrange(() => _uowMock.Class.Add(
    Arg.Matches<ModelClass>(x => (CheckArgs(x, updated)))))
    .DoNothing().Occurs(3);

....

protected static bool CheckArgs(ModelClass x, int y)
{
    if (x.val != y)
    {
        Assert.Fail("Houston we have a problem");
    }

    return true;
}
Roger
  • 427
  • 4
  • 13
0

Adding additional arrange before also worked for me but it is very hacky:

Mock.Arrange(() => activityListenerMock.PeriodPassed(Arg.IsAny<Period>())).
  DoInstead((Period p) => Console.WriteLine("Actual " + p.Length+" expected "+expectedLength));
Krzysztof Kot
  • 648
  • 1
  • 10
  • 15
0

Ran into this same dilemma today and started expanding on Krzysztof's idea some with an extension. It's rough but functional.

public static class JustMockExtensions {
        public static FuncExpectation<T> PrintParams<T, T1>(this FuncExpectation<T> mock) {
            return mock.DoInstead<T1, T>((arg1, arg2) => {
                string message = string.Empty;
                message += Process(arg1);
                message += Process(arg2);
                Console.WriteLine(message);
            });
        }

        private static string Process<T>(T obj) {
            if (typeof(T).IsEnum) {
                return Enum.GetName(typeof(T), obj);
            }
            return obj.ToString();
        }
    }

So far using it this way allows for it be piped along in the normal flow.

Mock.Arrange(() => foo.bar(Arg.IsAny<Widget>(), Arg.IsAny<WidgetTypeEnum>()))
                .PrintParams<Widget, WidgetTypeEnum>()
                .MustBeCalled();