Skip to content

Commit

Permalink
Add VerifyNoOtherCalls to MockFactory (#682)
Browse files Browse the repository at this point in the history
  • Loading branch information
BlythMeister authored and stakx committed Sep 7, 2018
1 parent a8e2642 commit 22ed239
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/Moq/Mock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
19 changes: 17 additions & 2 deletions src/Moq/MockException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,14 +193,29 @@ internal static MockException UnmatchedSetups(IEnumerable<MockException> errors)
/// <summary>
/// Returns the exception to be thrown when <see cref="Mock.VerifyNoOtherCalls(Mock)"/> finds invocations that have not been verified.
/// </summary>
internal static MockException UnverifiedInvocations(IEnumerable<Invocation> invocations)
internal static MockException UnverifiedInvocations(Mock mock, IEnumerable<Invocation> 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()));
}

/// <summary>
/// Returns the exception to be thrown when <see cref="MockFactory.VerifyNoOtherCalls()"/> finds invocations that have not been verified.
/// </summary>
public static Exception UnverifiedInvocations(List<MockException> 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;
Expand Down
31 changes: 29 additions & 2 deletions src/Moq/Obsolete/MockFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,35 @@ public virtual void VerifyAll()
}

/// <summary>
/// Invokes <paramref name="verifyAction"/> for each mock
/// in <see cref="Mocks"/>, and accumulates the resulting
/// Calls <see cref="Mock{T}.VerifyNoOtherCalls()"/> on all mocks created by this factory.
/// </summary>
/// <seealso cref="Mock{T}.VerifyNoOtherCalls()"/>
/// <exception cref="MockException">One or more mocks had invocations that were not verified.</exception>
public void VerifyNoOtherCalls()
{
var errors = new List<MockException>();

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);
}
}

/// <summary>
/// Invokes <paramref name="verifyAction"/> for each mock
/// in <see cref="Mocks"/>, and accumulates the resulting
/// verification exceptions that might be
/// thrown from the action.
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions src/Moq/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ Expected invocation on the mock once, but was {4} times: {1}</value>
<value>Invalid callback. Setup on method with {0} parameter(s) cannot invoke callback with different number of parameters ({1}).</value>
</data>
<data name="UnverifiedInvocations" xml:space="preserve">
<value>The following invocations were not verified:
{0}</value>
<value>The following invocations on mock '{0}' were not verified:
{1}</value>
</data>
</root>
85 changes: 69 additions & 16 deletions tests/Moq.Tests/MockRepositoryFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,61 @@ public void ShouldVerifyAll()
}
}

[Fact]
public void ShouldVerifyNoOtherCalls()
{
var repository = new MockRepository(MockBehavior.Default);
repository.Create<IFoo>();

repository.VerifyNoOtherCalls();
}

[Fact]
public void ShouldVerifyNoOtherCalls_WhenUnmatched()
{
var repository = new MockRepository(MockBehavior.Default);
var mock = repository.Create<IFoo>();

mock.Object.Do();

var mex = Assert.Throws<MockException>(() => repository.VerifyNoOtherCalls());
Assert.Equal(MockExceptionReason.UnverifiedInvocations, mex.Reason);
}

[Fact]
public void ShouldVerifyNoOtherCalls_WhenVerified()
{
var repository = new MockRepository(MockBehavior.Default);
var mock = repository.Create<IFoo>();

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<IFoo>();
var barMock = repository.Create<IBar>();

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()
{
Expand All @@ -76,25 +131,23 @@ public void ShouldVerifyVerifiables()
[Fact]
public void ShouldAggregateFailures()
{
try
{
var repository = new MockRepository(MockBehavior.Loose);
var foo = repository.Create<IFoo>();
var bar = repository.Create<IBar>();
var repository = new MockRepository(MockBehavior.Loose);
var foo = repository.Create<IFoo>();
var bar = repository.Create<IBar>();

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<Action<IFoo>> fooExpect = f => f.Do();
Assert.Contains(fooExpect.ToString(), mex.Message);
var ex = Record.Exception(() => repository.VerifyAll());

Expression<Action<IBar>> 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]
Expand Down

0 comments on commit 22ed239

Please sign in to comment.