From 22ed239b0e6f6bcff6db266d3e3891eaa0de4cc8 Mon Sep 17 00:00:00 2001 From: Chris Blyth Date: Fri, 7 Sep 2018 08:37:30 +0100 Subject: [PATCH] Add VerifyNoOtherCalls to MockFactory (#682) --- CHANGELOG.md | 1 + src/Moq/Mock.cs | 2 +- src/Moq/MockException.cs | 19 +++++- src/Moq/Obsolete/MockFactory.cs | 31 ++++++++- src/Moq/Properties/Resources.resx | 4 +- tests/Moq.Tests/MockRepositoryFixture.cs | 85 +++++++++++++++++++----- 6 files changed, 119 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5180b1d7c..188478335 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1 * `ExpressionCompiler`: An extensibility point for setting up alternate LINQ expression tree compilation strategies (@stakx, #647) * `setup.CallBase()` for `void` methods (@stakx, #664) +* `VerifyNoOtherCalls` for `MockRepository` (@BlythMeister, #682) #### Changed diff --git a/src/Moq/Mock.cs b/src/Moq/Mock.cs index 4ac27d9e0..13e128ca9 100644 --- a/src/Moq/Mock.cs +++ b/src/Moq/Mock.cs @@ -415,7 +415,7 @@ internal static void VerifyNoOtherCalls(Mock mock) var remainingUnverifiedInvocations = unverifiedInvocations.Where(i => i != null); if (remainingUnverifiedInvocations.Any()) { - throw MockException.UnverifiedInvocations(remainingUnverifiedInvocations); + throw MockException.UnverifiedInvocations(mock, remainingUnverifiedInvocations); } } diff --git a/src/Moq/MockException.cs b/src/Moq/MockException.cs index 7054932a6..932ddcd43 100644 --- a/src/Moq/MockException.cs +++ b/src/Moq/MockException.cs @@ -193,14 +193,29 @@ internal static MockException UnmatchedSetups(IEnumerable errors) /// /// Returns the exception to be thrown when finds invocations that have not been verified. /// - internal static MockException UnverifiedInvocations(IEnumerable invocations) + internal static MockException UnverifiedInvocations(Mock mock, IEnumerable invocations) { return new MockException( MockExceptionReason.UnverifiedInvocations, string.Format( CultureInfo.CurrentCulture, Resources.UnverifiedInvocations, - string.Join(Environment.NewLine, invocations))); + mock.ToString(), + invocations.Aggregate(new StringBuilder(), (builder, setup) => builder.AppendLine(setup.ToString())).ToString())); + } + + /// + /// Returns the exception to be thrown when finds invocations that have not been verified. + /// + public static Exception UnverifiedInvocations(List errors) + { + Debug.Assert(errors.All(e => e.Reason == MockExceptionReason.UnverifiedInvocations)); + + return new MockException( + MockExceptionReason.UnverifiedInvocations, + string.Join( + Environment.NewLine, + errors.Select(error => error.Message))); } private MockExceptionReason reason; diff --git a/src/Moq/Obsolete/MockFactory.cs b/src/Moq/Obsolete/MockFactory.cs index 1bc73d36e..4cbf2a5c1 100644 --- a/src/Moq/Obsolete/MockFactory.cs +++ b/src/Moq/Obsolete/MockFactory.cs @@ -363,8 +363,35 @@ public virtual void VerifyAll() } /// - /// Invokes for each mock - /// in , and accumulates the resulting + /// Calls on all mocks created by this factory. + /// + /// + /// One or more mocks had invocations that were not verified. + public void VerifyNoOtherCalls() + { + var errors = new List(); + + foreach (var mock in mocks) + { + try + { + Mock.VerifyNoOtherCalls(mock); + } + catch (MockException error) when (error.Reason == MockExceptionReason.UnverifiedInvocations) + { + errors.Add(error); + } + } + + if (errors.Count > 0) + { + throw MockException.UnverifiedInvocations(errors); + } + } + + /// + /// Invokes for each mock + /// in , and accumulates the resulting /// verification exceptions that might be /// thrown from the action. /// diff --git a/src/Moq/Properties/Resources.resx b/src/Moq/Properties/Resources.resx index a5e15c6f0..e2a1e4bc9 100644 --- a/src/Moq/Properties/Resources.resx +++ b/src/Moq/Properties/Resources.resx @@ -345,7 +345,7 @@ Expected invocation on the mock once, but was {4} times: {1} Invalid callback. Setup on method with {0} parameter(s) cannot invoke callback with different number of parameters ({1}). - The following invocations were not verified: -{0} + The following invocations on mock '{0}' were not verified: +{1} \ No newline at end of file diff --git a/tests/Moq.Tests/MockRepositoryFixture.cs b/tests/Moq.Tests/MockRepositoryFixture.cs index 96b9273e0..6820db550 100644 --- a/tests/Moq.Tests/MockRepositoryFixture.cs +++ b/tests/Moq.Tests/MockRepositoryFixture.cs @@ -52,6 +52,61 @@ public void ShouldVerifyAll() } } + [Fact] + public void ShouldVerifyNoOtherCalls() + { + var repository = new MockRepository(MockBehavior.Default); + repository.Create(); + + repository.VerifyNoOtherCalls(); + } + + [Fact] + public void ShouldVerifyNoOtherCalls_WhenUnmatched() + { + var repository = new MockRepository(MockBehavior.Default); + var mock = repository.Create(); + + mock.Object.Do(); + + var mex = Assert.Throws(() => repository.VerifyNoOtherCalls()); + Assert.Equal(MockExceptionReason.UnverifiedInvocations, mex.Reason); + } + + [Fact] + public void ShouldVerifyNoOtherCalls_WhenVerified() + { + var repository = new MockRepository(MockBehavior.Default); + var mock = repository.Create(); + + mock.Object.Do(); + + mock.Verify(foo => foo.Do(), Times.Once); + repository.VerifyNoOtherCalls(); + } + + [Fact] + public void VerifyNoOtherCalls_should_correctly_aggregate_unmatched_calls_from_more_than_one_mock() + { + var repository = new MockRepository(MockBehavior.Default); + var fooMock = repository.Create(); + var barMock = repository.Create(); + + fooMock.Object.Do(); + barMock.Object.Redo(); + + var ex = Record.Exception(() => repository.VerifyNoOtherCalls()); + + var expectedErrorMessage = $"The following invocations on mock \'{fooMock}\' were not verified:{Environment.NewLine}" + + $"IFoo.Do(){Environment.NewLine}" + + Environment.NewLine + + $"The following invocations on mock \'{barMock}\' were not verified:{Environment.NewLine}" + + $"IBar.Redo(){Environment.NewLine}"; + + Assert.NotNull(ex); + Assert.Equal(expectedErrorMessage, ex.Message); + } + [Fact] public void ShouldVerifyVerifiables() { @@ -76,25 +131,23 @@ public void ShouldVerifyVerifiables() [Fact] public void ShouldAggregateFailures() { - try - { - var repository = new MockRepository(MockBehavior.Loose); - var foo = repository.Create(); - var bar = repository.Create(); + var repository = new MockRepository(MockBehavior.Loose); + var foo = repository.Create(); + var bar = repository.Create(); - foo.Setup(f => f.Do()); - bar.Setup(b => b.Redo()); + foo.Setup(f => f.Do()); + bar.Setup(b => b.Redo()); - repository.VerifyAll(); - } - catch (MockException mex) - { - Expression> fooExpect = f => f.Do(); - Assert.Contains(fooExpect.ToString(), mex.Message); + var ex = Record.Exception(() => repository.VerifyAll()); - Expression> barExpect = b => b.Redo(); - Assert.Contains(barExpect.ToString(), mex.Message); - } + var expectedErrorMessage = $"The following setups on mock \'{foo}\' were not matched:{Environment.NewLine}" + + $"IFoo f => f.Do(){Environment.NewLine}" + + Environment.NewLine + + $"The following setups on mock \'{bar}\' were not matched:{Environment.NewLine}" + + $"IBar b => b.Redo(){Environment.NewLine}"; + + Assert.NotNull(ex); + Assert.Equal(expectedErrorMessage, ex.Message); } [Fact]