/// <summary> /// Returns the exception to be thrown when <see cref="Mock.Verify"/> finds no invocations (or the wrong number of invocations) that match the specified expectation. /// </summary> internal static MockException NoMatchingCalls( Mock rootMock, LambdaExpression expression, string failMessage, Times times, int callCount) { var message = new StringBuilder(); message.AppendLine(failMessage ?? "") .Append(times.GetExceptionMessage(callCount)) .AppendLine(expression.PartialMatcherAwareEval().ToStringFixed()) .AppendLine() .AppendLine(Resources.PerformedInvocations) .AppendLine(); var visitedMocks = new HashSet <Mock>(); var mocks = new Queue <Mock>(); mocks.Enqueue(rootMock); while (mocks.Any()) { var mock = mocks.Dequeue(); if (visitedMocks.Contains(mock)) { continue; } visitedMocks.Add(mock); message.AppendLine(mock == rootMock ? $" {mock} ({expression.Parameters[0].Name}):" : $" {mock}:"); var invocations = mock.MutableInvocations.ToArray(); if (invocations.Any()) { message.AppendLine(); foreach (var invocation in invocations) { message.Append($" {invocation}"); if (invocation.Method.ReturnType != typeof(void) && Unwrap.ResultIfCompletedTask(invocation.ReturnValue) is IMocked mocked) { var innerMock = mocked.Mock; mocks.Enqueue(innerMock); message.Append($" => {innerMock}"); } message.AppendLine(); } } else { message.AppendLine($" {Resources.NoInvocationsPerformed}"); } message.AppendLine(); } return(new MockException(MockExceptionReasons.NoMatchingCalls, message.TrimEnd().AppendLine().ToString())); }