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

Add WillReceive.InOrder() for evaluating call sequence expectations at execution time #863

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

loop8ack
Copy link

This PR introduces WillReceive.InOrder() to solve the issue where mutable objects lead to false negatives in ordered call verifications. The core problem is that Received.InOrder() evaluates argument matchers at verification time instead of call time, causing tests to fail when object references are modified during test execution.

This addresses the long-standing issue #392 and implements the solution I proposed in #861.

The syntax of this feature:

var action = Substitute.For<IAction>();
var person = new Person() { Name = "John" };

WillReceive
    .InOrder(() =>
    {
        action.Act(Arg.Is<Person>(p => p.Name == "John"));
        action.Act(Arg.Is<Person>(p => p.Name == "Joe"));
    })
    .WhileExecuting(() =>
    {
        action.Act(person);
        person.Name = "Doe";
        action.Act(person);
        person.Name = "Hans";
    });

Produces clear error messages showing the exact point of failure:

Call 1: Accepted!
Call 2: Not matched!
    Expected: Act(p => (p.Name == "Joe"))
    But was: Act(Person)
        arg[0] not matched: p => (p.Name == "Joe")

The goal was to provide a solution that evaluates argument matchers at the time of the call while staying close to NSubstitute's existing syntax. Instead of capturing object states or requiring manual workarounds, the implementation directly solves the timing issue without compromising NSubstitute's simple and intuitive API style.

Let me know if you'd like me to make any adjustments to the implementation or tests.

@loop8ack
Copy link
Author

loop8ack commented Feb 2, 2025

I discovered a significant limitation with the current implementation: Because it uses IQuery internally, which takes precedence over other routes in NSubstitute's routing system, any mock configuration set up before WillReceive.InOrder() won't be applied to calls within WhileExecuting(). For example:

// This configuration won't affect calls inside WhileExecuting
action.When(x => x.SomeMethod()).Do(_ => /* ... */);

WillReceive
    .InOrder(() => /* ... */)
    .WhileExecuting(() =>
    {
        // The configured callback won't be executed here
        action.SomeMethod();
    });

This is a significant limitation as it prevents using configured mocks together with this new feature. I currently don't have a solution for this and would appreciate suggestions on how to properly integrate with NSubstitute's routing system.

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.

1 participant