diff --git a/src/Moq/AutoImplementedPropertyGetterSetup.cs b/src/Moq/AutoImplementedPropertyGetterSetup.cs index 3efeec038..fc3e27430 100644 --- a/src/Moq/AutoImplementedPropertyGetterSetup.cs +++ b/src/Moq/AutoImplementedPropertyGetterSetup.cs @@ -32,15 +32,5 @@ public override bool TryGetReturnValue(out object returnValue) returnValue = this.getter.Invoke(); return true; } - - public override MockException TryVerify() - { - return this.TryVerifyInnerMock(innerMock => innerMock.TryVerify()); - } - - public override MockException TryVerifyAll() - { - return this.TryVerifyInnerMock(innerMock => innerMock.TryVerifyAll()); - } } } diff --git a/src/Moq/AutoImplementedPropertySetterSetup.cs b/src/Moq/AutoImplementedPropertySetterSetup.cs index d50d23cb1..9b65cc49b 100644 --- a/src/Moq/AutoImplementedPropertySetterSetup.cs +++ b/src/Moq/AutoImplementedPropertySetterSetup.cs @@ -26,7 +26,5 @@ public override void Execute(Invocation invocation) this.setter.Invoke(invocation.Arguments[0]); invocation.Return(); } - - public override MockException TryVerifyAll() => null; } } diff --git a/src/Moq/InnerMockSetup.cs b/src/Moq/InnerMockSetup.cs index 76270c77f..77866264e 100644 --- a/src/Moq/InnerMockSetup.cs +++ b/src/Moq/InnerMockSetup.cs @@ -24,16 +24,6 @@ public override bool TryGetReturnValue(out object returnValue) return true; } - public override MockException TryVerify() - { - return this.TryVerifyInnerMock(innerMock => innerMock.TryVerify()); - } - - public override MockException TryVerifyAll() - { - return this.TryVerifyInnerMock(innerMock => innerMock.TryVerifyAll()); - } - public override void Uninvoke() { if (this.ReturnsInnerMock(out var innerMock)) diff --git a/src/Moq/Interception/InterceptionAspects.cs b/src/Moq/Interception/InterceptionAspects.cs index 682e20b7e..285690a5f 100644 --- a/src/Moq/Interception/InterceptionAspects.cs +++ b/src/Moq/Interception/InterceptionAspects.cs @@ -104,15 +104,7 @@ public static bool Handle(Invocation invocation, Mock mock) if (matchedSetup != null) { matchedSetup.EvaluatedSuccessfully(invocation); - - if (matchedSetup.IsVerifiable) - { - invocation.MarkAsMatchedByVerifiableSetup(); - } - else - { - invocation.MarkAsMatchedBySetup(); - } + invocation.MarkAsMatchedBy(matchedSetup); matchedSetup.SetOutParameters(invocation); diff --git a/src/Moq/Invocation.cs b/src/Moq/Invocation.cs index 6d7c6b5fa..a5edd42a1 100644 --- a/src/Moq/Invocation.cs +++ b/src/Moq/Invocation.cs @@ -16,7 +16,8 @@ internal abstract class Invocation : IInvocation private MethodInfo methodImplementation; private readonly Type proxyType; private object returnValue; - private VerificationState verificationState; + private Setup matchingSetup; + private bool verified; /// /// Initializes a new instance of the class. @@ -68,7 +69,7 @@ public MethodInfo MethodImplementation public object ReturnValue => this.returnValue; - internal bool Verified => this.verificationState == VerificationState.Verified; + internal bool Verified => this.verified; /// /// Ends the invocation as if a statement occurred. @@ -107,39 +108,20 @@ public MethodInfo MethodImplementation /// public abstract void Return(object value); - internal void MarkAsMatchedBySetup() // this supports the `mock.VerifyAll()` machinery + internal void MarkAsMatchedBy(Setup setup) { - if (this.verificationState == VerificationState.Invoked) - { - this.verificationState = VerificationState.InvokedAndMatchedBySetup; - } - } + Debug.Assert(this.matchingSetup == null); - internal void MarkAsMatchedByVerifiableSetup() // this supports the `mock.Verify()` machinery - { - if (this.verificationState == VerificationState.Invoked || - this.verificationState == VerificationState.InvokedAndMatchedBySetup) - { - this.verificationState = VerificationState.InvokedAndMatchedByVerifiableSetup; - } + this.matchingSetup = setup; } - internal void MarkAsVerified() => this.verificationState = VerificationState.Verified; - - internal void MarkAsVerifiedIfMatchedBySetup() // this supports the `mock.VerifyAll()` machinery - { - if (this.verificationState == VerificationState.InvokedAndMatchedBySetup || - this.verificationState == VerificationState.InvokedAndMatchedByVerifiableSetup) - { - this.verificationState = VerificationState.Verified; - } - } + internal void MarkAsVerified() => this.verified = true; - internal void MarkAsVerifiedIfMatchedByVerifiableSetup() // this supports the `mock.Verify()` machinery + internal void MarkAsVerifiedIfMatchedBy(Func predicate) { - if (this.verificationState == VerificationState.InvokedAndMatchedByVerifiableSetup) + if (this.matchingSetup != null && predicate(this.matchingSetup)) { - this.verificationState = VerificationState.Verified; + this.verified = true; } } @@ -189,13 +171,5 @@ public override string ToString() return builder.ToString(); } - - private enum VerificationState : byte - { - Invoked = 0, - InvokedAndMatchedBySetup, - InvokedAndMatchedByVerifiableSetup, - Verified, - } } } diff --git a/src/Moq/MethodCall.cs b/src/Moq/MethodCall.cs index 9b71745ee..6b103cb12 100644 --- a/src/Moq/MethodCall.cs +++ b/src/Moq/MethodCall.cs @@ -331,9 +331,10 @@ public void SetThrowExceptionResponse(Exception exception) this.returnOrThrowResponse = new ThrowExceptionResponse(exception); } - public override MockException TryVerifyAll() + protected override bool TryVerifySelf(out MockException error) { - return (this.flags & Flags.Invoked) == 0 ? MockException.UnmatchedSetup(this) : null; + error = (this.flags & Flags.Invoked) != 0 ? null : MockException.UnmatchedSetup(this); + return error == null; } public override void Uninvoke() diff --git a/src/Moq/Mock.cs b/src/Moq/Mock.cs index 681206a09..9a9449639 100644 --- a/src/Moq/Mock.cs +++ b/src/Moq/Mock.cs @@ -260,11 +260,7 @@ public DefaultValue DefaultValue /// public void Verify() { - var error = this.TryVerify(); - if (error?.IsVerificationError == true) - { - throw error; - } + this.Verify(setup => setup.IsVerifiable); } /// @@ -289,55 +285,45 @@ public void Verify() /// public void VerifyAll() { - var error = this.TryVerifyAll(); - if (error?.IsVerificationError == true) - { - throw error; - } + this.Verify(setup => true); } - internal MockException TryVerify() + private void Verify(Func predicate) { - foreach (Invocation invocation in this.MutableInvocations) + if (!this.TryVerify(predicate, out var error) && error.IsVerificationError) { - invocation.MarkAsVerifiedIfMatchedByVerifiableSetup(); + throw error; } - - return this.TryVerifySetups(setup => setup.TryVerify()); } - internal MockException TryVerifyAll() + internal bool TryVerify(Func predicate, out MockException error) { foreach (Invocation invocation in this.MutableInvocations) { - invocation.MarkAsVerifiedIfMatchedBySetup(); + invocation.MarkAsVerifiedIfMatchedBy(predicate); } - return this.TryVerifySetups(setup => setup.TryVerifyAll()); - } - - private MockException TryVerifySetups(Func verifySetup) - { var errors = new List(); foreach (var setup in this.Setups.ToArrayLive(_ => true)) { - var error = verifySetup(setup); - if (error?.IsVerificationError == true) + if (!setup.TryVerify(predicate, out var e) && e.IsVerificationError) { - errors.Add(error); + errors.Add(e); } } if (errors.Count > 0) { - return MockException.Combined( + error = MockException.Combined( errors, preamble: string.Format(CultureInfo.CurrentCulture, Resources.VerificationErrorsOfMock, this)); + return false; } else { - return null; + error = null; + return true; } } diff --git a/src/Moq/SequenceSetup.cs b/src/Moq/SequenceSetup.cs index 559871f10..5d757844b 100644 --- a/src/Moq/SequenceSetup.cs +++ b/src/Moq/SequenceSetup.cs @@ -93,9 +93,10 @@ public override void Execute(Invocation invocation) } } - public override MockException TryVerifyAll() + protected override bool TryVerifySelf(out MockException error) { - return this.invoked ? null : MockException.UnmatchedSetup(this); + error = this.invoked ? null : MockException.UnmatchedSetup(this); + return error == null; } public override void Uninvoke() diff --git a/src/Moq/Setup.cs b/src/Moq/Setup.cs index 988fce967..f3479075b 100644 --- a/src/Moq/Setup.cs +++ b/src/Moq/Setup.cs @@ -89,25 +89,79 @@ public override string ToString() return builder.ToString(); } - public virtual MockException TryVerify() + /// + /// Verifies this setup and/or those of its inner mock (if present and known). + /// + /// + /// Specifies which setups should be verified. + /// + /// + /// If this setup and/or any of its inner mock (if present and known) failed verification, + /// this parameter will receive a describing the verification error(s). + /// + /// + /// if verification succeeded without any errors; + /// otherwise, . + /// + public bool TryVerify(Func predicate, out MockException error) { - return this.IsVerifiable ? this.TryVerifyAll() : null; - } + if (predicate(this)) + { + if (!this.TryVerifySelf(out var e) && e.IsVerificationError) + { + error = e; + return false; + } + } - public abstract MockException TryVerifyAll(); + return this.TryVerifyInnerMock(predicate, out error); + } - public MockException TryVerifyInnerMock(Func verify) + /// + /// Verifies all setups of this setup's inner mock (if present and known). + /// Multiple verification errors are aggregated into a single . + /// + /// + /// Specifies which setups should be verified. + /// + /// + /// If one or more setups of this setup's inner mock (if present and known) failed verification, + /// this parameter will receive a describing the verification error(s). + /// + /// + /// if verification succeeded without any errors; + /// otherwise, . + /// + protected virtual bool TryVerifyInnerMock(Func predicate, out MockException error) { if (this.ReturnsInnerMock(out var innerMock)) { - var error = verify(innerMock); - if (error?.IsVerificationError == true) + if (!innerMock.TryVerify(predicate, out var e) && e.IsVerificationError) { - return MockException.FromInnerMockOf(this, error); + error = MockException.FromInnerMockOf(this, e); + return false; } } - return null; + error = null; + return true; + } + + /// + /// Verifies only this setup, excluding those of its inner mock (if present and known). + /// + /// + /// If this setup failed verification, + /// this parameter will receive a describing the verification error. + /// + /// + /// if verification succeeded without any errors; + /// otherwise, . + /// + protected virtual bool TryVerifySelf(out MockException error) + { + error = null; + return true; } public virtual void Uninvoke() diff --git a/tests/Moq.Tests/VerifyFixture.cs b/tests/Moq.Tests/VerifyFixture.cs index 09d579f20..7191ce74f 100644 --- a/tests/Moq.Tests/VerifyFixture.cs +++ b/tests/Moq.Tests/VerifyFixture.cs @@ -1486,6 +1486,23 @@ public void Verify_on_non_overridable_method_throws_NotSupportedException() mock.Verify(m => m.InvokePopulate(ref It.Ref.IsAny), Times.Never)); } + [Fact] + public void Verification_marks_invocations_of_inner_mocks_as_verified() + { + var mock = new Mock() { DefaultValue = DefaultValue.Mock }; + mock.Setup(m => m.Value).Returns(1); + mock.Setup(m => m.Bar.Value).Returns(2); + + // Invoke everything that has been set up, and verify everything: + _ = mock.Object.Value; + _ = mock.Object.Bar.Value; + mock.VerifyAll(); + + // The above call to `VerifyAll` should have marked all invocations as verified, + // including those on the inner `Bar` mock: + Mock.Get(mock.Object.Bar).VerifyNoOtherCalls(); + } + public class Exclusion_of_unreachable_inner_mocks { [Fact]