Closed MagnusMikkelsen closed 4 years ago
Thanks for the excellent repro case. 👍
Confirmed this behaviour on Mac, dotnetcore 2.0, NSub 4.2.1, AutoFixture 4.11.0.
I'm not sure exactly why this is happening from a quick look, but I think it is related to nested substitute configuration. Setting IBar.DefaultTrue
while configuring the IFoo
call makes NSubstitute think the call configuration is done while it is actually still configuring another call. (Similar to https://github.com/nsubstitute/NSubstitute/issues/217, https://github.com/nsubstitute/NSubstitute/issues/56.)
For the foo.GiveMeBarFromNumber(Arg.Any<int>())
call, the use of the arg matcher puts the substitute in a different, configuration mode. I need to look into why this fixes the problem, but for now you can try working around the issue by using NSubstitute.Extensions;
and calling Configure()
to put the substitute into that configuration mode for the GiveMeBar
call as well:
foo.Configure()
.GiveMeBar()
.Returns(bar);
Hope this helps get your test working again until I can get a better answer for you.
Thanks @MagnusMikkelsen for the nicely crafted scenario and @dtchepak for the brilliant analysis you did. The reasoning is exactly as described above. Upon receiving a call, NSubstitute remembers it as a "last" call, so that it could be configured by a subsequent call. In your case, the sequence is following:
GiveMeBar()
call is received and stored as lastr.DefaultTrue
getter call is received and stored as lastGiveMeBar()
method returnsReturns(bar)
tries to configure last call, which is wrong in our case.This is an architectural issue which is quite hard to solve. That's why we introduced the Configure()
method to provide NSubstitute with a hint that you actually don't use value returned from the call, as you are going to configure it. This way AutoFixture will not kick in, Register()
callback will be not invoked and last call info will be not rewritten.
It's very unpleasant side-effect on AutoFixture-NSubstitute boundary, but hopefully workaround helps to mitigate it.
Thanks for the explanation @dtchepak and @zvirja, and thank you for your hard work on this library, it's fantastic 👍
I guess I'll just use the .Configure()
workaround.
@MagnusMikkelsen Thanks! Let us know if you still need our assistance.
Description When setting the return value for a substitute method, where:
AutoFixture.Register()
callthe first Returns() method (
foo.GiveMeBar().Returns(bar)
in the example) throws a NSubstitute.Exceptions.CouldNotSetReturnDueToNoLastCallException.It is possible to workaround the problem by either
I have included these workarounds in the reproduction code as comments.
To Reproduce
Expected behaviour I did not expect any exception, because I called Returns() after calling my substitute, and I am not configuring other substitutes within Returns().
Environment: