-
Notifications
You must be signed in to change notification settings - Fork 275
Description
Is your feature request related to a problem?
I'm encountering the same issue as described in #392 where Received.InOrder
evaluates argument matchers at assertion time rather than at the time of the actual call. This leads to false negatives when testing with mutable objects that change during the test.
Consider this example:
var action = Substitute.For<IAction>();
var person = new Person() { Name = "John" };
action.Act(person);
person.Name = "Doe";
action.Act(person);
person.Name = "Hans";
// This fails because both calls are evaluated with person.Name == "Hans"
Received.InOrder(() =>
{
action.Act(Arg.Is<Person>(p => p.Name == "John"));
action.Act(Arg.Is<Person>(p => p.Name == "Doe"));
});
In my case, the argument matchers are far more complex than in this simplified example, making workarounds like capturing the state manually impractical.
Describe the solution you'd like
I've started developing a solution like this:
var action = Substitute.For<IAction>();
var person = new Person() { Name = "John" };
// Configure expectations before any calls happen
var verifier = WillReceive.InOrder(action,
desc =>
{
desc.Call(x => x.Act(Arg.Is<Person>(p => p.Name == "John")));
desc.Call(x => x.Act(Arg.Is<Person>(p => p.Name == "Doe")));
});
// Each call will be verified in the order it was received.
// If there were any calls skipped, an exception will be thrown.
action.Act(person);
person.Name = "Doe";
action.Act(person);
person.Name = "Hans";
// Are any calls missing at the end of the test?
verifier.Verify();
The core concept is to register the expected calls beforehand and use NSubstitute's .When().Do()
to immediately evaluate each call as it happens, storing just a simple boolean result rather than capturing or cloning the arguments. The approach provides reliable verification of mutable objects while completely avoiding the serialization concerns raised in #392 .
I've started building this as a separate utility that works around NSubstitute, but I believe it would be much more valuable as a native feature. The concept is flexible enough to support various verification scenarios while maintaining the simple and intuitive API style of NSubstitute.
I'd be happy to contribute this as a pull request once the design is agreed upon and would appreciate feedback on whether this approach aligns with NSubstitute's philosophy and any suggestions for better integration with the existing codebase.