devlooped / devlooped/moq

`.Verify()` on one mock complains over missing invocations on another mock

Open
#1,699 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
C#
Stars
6.4k
Forks
835
PR merge metrics
No merged PRs in 30d

Description

(I know this is really a kind of duplicate of #1018, but since I am not authorized to comment on the closed issue, I am communicating it here.)

We had a test that was written like below (here simplified). Essentially the same as #1018 (hence the namespace), but in this case the "extra" .Setup is there for a legitimate reason. Here written without the use of MockRepository.

Steps to Reproduce

This full console app (C#) illustrates the situation:

using Moq;

namespace Moq1018;

static class Program {
    static void Main() {
        // set up all
        var mockOne = new Mock<IOne>(MockBehavior.Strict);
        var mockTwo = new Mock<ITwo>(MockBehavior.Strict);
        mockOne.Setup(x => x.GetSomething()).Returns(mockTwo.Object).Verifiable();
        mockTwo.Setup(x => x.DoSomething()).Verifiable();

        // make SUT
        var objectToTest = new ObjectToTest(mockOne.Object);

        // call first method and verify
        objectToTest.FirstMethod();
        mockOne.Verify();

        // call second method and verify
        objectToTest.SecondMethod();
        mockTwo.Verify();
    }
}

class ObjectToTest(IOne one) {
    ITwo? _Member;

    public void FirstMethod() {
        _Member = one.GetSomething();
    }

    public void SecondMethod() {
        _Member?.DoSomething();
    }
}

public interface IOne {
    ITwo GetSomething();
}
public interface ITwo {
    void DoSomething();
}

Expected Behavior

It feels the above test (just called Main here) should complete without failure.

Actual Behavior

When control reaches mockOne.Verify(), an exception is thrown because the setup on mockTwo has not been met (yet)!

Known Workarounds

  1. If we change overload of .Returns and write .Returns(() => mockTwo.Object) instead of simply .Returns(mockTwo.Object), then it works as expected. Saw this workaround in #1018, but it feels unnatural that this change should lead to another outcome.
  2. If we move the mockTwo.Setup statement several lines down, to after the mockOne.Verify call, then the code runs without issue. This may give clearer test code in some cases, but above, the author wanted to keep all setups together in a single "section" of the test method.
  3. If we move the mockOne.Verify() down to after the call to objectToTest.SecondMethod(), then it works. But then we cannot really prove if it was FirstMethod() or SecondMethod() that did GetSomething.
  4. Maybe there is an entirely different way to write the test that I had not thought of?

Version Info

Moq 4.20.72

Back this issue
Back this issue

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the standalone console app in the issue and observe the sequence through Program.Main, ObjectToTest.FirstMethod, and ObjectToTest.SecondMethod. Then trace how Setup, Returns, and Verify handle the two mocks; done means mockOne.Verify() succeeds before the second method while mockTwo.Verify() still confirms its later invocation.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.