Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VerifyNoOtherCalls: fix bugs with multi-dot expressions #544

Merged
merged 5 commits into from
Dec 4, 2017

Conversation

stakx
Copy link
Contributor

@stakx stakx commented Dec 4, 2017

This PR ensures that VerifyNoOtherCalls gets along more or less nicely with multi-dot ("recursive") verification expressions.

Recursive expressions are generally a pain point in Moq, and the way they are implemented lead to all sorts of trouble. Quite a few of the currently remaining bug reports are directly related to that particular aspect of Moq. I'm expecting that despite the improvements in this PR, people might sooner or later discover that VerifyNoOtherCalls acts a little weird in circumstances where multi-dot expressions and automatically mocked sub-objects are involved.

(Fixing these multi-dot expression troubles is semi-planned for Moq 4.9 or 4.10.)

This solves the second of two issues with the initial VerifyNoOtherCalls implementation, as tracked in #527 (comment).

Using recursive (multi-dot) `Verify` expressions can result in the
implicit creation of "inner mocks" (and, depending on whether `Verify`
receives an expression tree or an action delegate) invocations to
get at the sub-objects:

    mock.Verify(m => m.A.B.C.X);
    mock.VerifyNoOtherCalls();

One would probably expect that invocations for `m.A`, `m.A.B`, and
`m.A.B.C` were implicitly verified in the first `Verify` call.

The tests added in this commit show that this doesn't work yet.
(Also, `VerifyNoOtherCalls` does not perform recursive verification,
but just like `Verify` and `VerifyAll`, perhaps it should?)
Like `Verify` and `VerifyAll`, `VerifyNoOtherCalls` should really
perform recursive verification; that is, it should include invocations
on any automatically mocked sub-objects created through multi-dot
expressions.

The test added here shows that this doesn't work yet.
The current implementation of `VerifyNoOtherCalls` has one short-
coming, which is that if a method is invoked in both a "transitive"
manner but also non-transitively, then the latter cannot be recognized
as such. For example:

    var mock = new Mock<IFoo> { DefaultValue = DefaultValue.Mock };

    mock.Object.A().B();
    mock.Object.A();

    mock.Verify(m => m.A().B());
    mock.VerifyNoOtherCalls();

With the above, `VerifyNoOtherCalls` won't currently be able to recog-
nise the second, "non-transitive" call to `.A()` as one that needs to
be explicitly verified. No verification exception will be thrown.

Add tests that document both this problem, and a possible way of fix-
ing it.
@stakx stakx merged commit 817ef36 into devlooped:develop Dec 4, 2017
@stakx stakx deleted the verify-no-other-calls branch December 4, 2017 21:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant