NSubstitute
NSubstitute copied to clipboard
Improve Received.InOrder assertion failure message when there are non-matching calls
Hi, I'm trying to use Argument Matcher in Received.InOrder, but the unit test result window didn't show the wrong argument as they do outside Received.InOrder. Is there any way to retrieve the wrong argument and show it in the unit test result?
public void Do()
{
_test.A(new CustomObj());
_test.B(new CustomObj());
_test.A(new CustomObj());
}
public void Test()
{
_program.Do();
_test.A(Arg.Any<CustomObj>());
_test.B(Arg.Any<CustomObj>());
Received.InOrder((() =>
{
_test.A(Arg.Is<CustomObj>(x => x.Prop1 == 11));
_test.B(Arg.Is<CustomObj>(x => x.Prop1 == 22));
_test.A(Arg.Is<CustomObj>(x => x.Prop1 == 33));
}));
}
public void Test2()
{
_program.Do();
_test.A(Arg.Any<CustomObj>());
_test.B(Arg.Any<CustomObj>());
_test.Received(2).A(Arg.Is<CustomObj>(x => x.Prop1 == 11));
_test.Received(1).B(Arg.Is<CustomObj>(x => x.Prop1 == 22));
}
For example, Test1 will only show
NSubstitute.Exceptions.CallSequenceNotFoundException :
Expected to receive these calls in order:
A(x => (x.Prop1 == 11))
B(x => (x.Prop1 == 22))
A(x => (x.Prop1 == 33))
Actually received matching calls in this order:
while Test2 will show non-matching arguments
NSubstitute.Exceptions.ReceivedCallsException : Expected to receive exactly 2 calls matching:
A(x => (x.Prop1 == 11))
Actually received no matching calls.
Received 2 non-matching calls (non-matching arguments indicated with '*' characters):
A(*CustomObj*)
A(*CustomObj*)
Hi @vivi00790 ,
This looks like a bug. Thank you for raising it!
Testing notes:
I was able to reproduce this with:
public interface IExample {
void A(CustomObj customObj);
void B(CustomObj customObj);
}
public class CustomObj {
public int Prop1 { get; set; }
}
[Fact]
public void Test() {
var test = Substitute.For<IExample>();
test.A(new CustomObj());
test.B(new CustomObj());
test.A(new CustomObj());
Received.InOrder(() => {
test.A(Arg.Is<CustomObj>(x => x.Prop1 == 11));
test.B(Arg.Is<CustomObj>(x => x.Prop1 == 22));
test.A(Arg.Is<CustomObj>(x => x.Prop1 == 33));
});
}
If we switch to an Arg.Any for A the message seems to display correctly for that member, but the call to B still does not appear:
[Fact]
public void Test2() {
var test = Substitute.For<IExample>();
test.A(new CustomObj());
test.B(new CustomObj());
test.A(new CustomObj());
Received.InOrder(() => {
test.A(Arg.Any<CustomObj>());
test.B(Arg.Is<CustomObj>(x => x.Prop1 == 22));
test.A(Arg.Is<CustomObj>(x => x.Prop1 == 33));
});
}
/*
NSubstitute.Exceptions.CallSequenceNotFoundException :
Expected to receive these calls in order:
A(any AnotherFixture+CustomObj)
B(x => (x.Prop1 == 22))
A(x => (x.Prop1 == 33))
Actually received matching calls in this order:
A(AnotherFixture+CustomObj)
A(AnotherFixture+CustomObj)
*/
Just noticed the example code has the _test.A(Arg.Any...) lines as per the other issue raised. This bug does not seem related to that as this example shows the bug without this.
Testing this further, realised that the actual calls listed are "received matching calls in this order", so only calls that match Prop1 == 11, Prop1 == 22, or Prop1 == 33 will be displayed.
I will re-label this as a feature request to display more information for Received.InOrder assertion failures.
At the very least we should say something like "Actually received no calls matching these criteria." if there are calls satisfying those arg matchers.
Testing this further, realised that the actual calls listed are "received matching calls in this order", so only calls that match
Prop1 == 11,Prop1 == 22, orProp1 == 33will be displayed.I will re-label this as a feature request to display more information for
Received.InOrderassertion failures.At the very least we should say something like "Actually received no calls matching these criteria." if there are calls satisfying those arg matchers.
Hi, @dtchepak thanks for the detailed reply! I do found that I forget to set Prop1 in Do(), correct code should be:
var a = _test.A(new CustomObj() { Prop1 = 11 });
var b = _test.B(new CustomObj() { Prop1 = 22 });
var c = _test.A(new CustomObj() { Prop1 = 33 });
return a + b + c;
Though I tried with this new code and adjust
_test.Received(1).B(Arg.Is<CustomObj>(x => x.Prop1 == 22))
to
_test.Received(1).B(Arg.Is<CustomObj>(x => x.Prop1 == 55))
the test returns:
NSubstitute.Exceptions.CallSequenceNotFoundException :
Expected to receive these calls in order:
A(x => (x.Prop1 == 11))
B(x => (x.Prop1 == 55))
A(x => (x.Prop1 == 33))
Actually received matching calls in this order:
A(CustomObj)
A(CustomObj)
The exact information I'd like to know is
- What calls does the stub actually receive
- Received matching and non-matching calls and their arguments
For example:
Expected to receive these calls in order:
A(x => (x.Prop1 == 11))
B(x => (x.Prop1 == 55))
A(x => (x.Prop1 == 33))
Actually received calls in this order:
A(CustomObj) --Matched
B(*Failed criteria: Prop1 == 55*)
A(CustomObj) --Matched
This information can help to which part of the test actually goes wrong (mis-config the stub, generating wrong expect object, etc) or there's some problem in code under testing.
I'd like to bump this a year later and stress the importance of this. I'm completely unable to fix a test I'm working on right now because I have no idea why it doesn't say that a call is matched. Best I can tell it matches fine, but I don't get any additional diagnostic to help me. @vivi00790 explained it perfectly well and I have the same 2 goals.