To get the NSubstitute syntax to work there is some messiness going on behind the scenes. This is one of those cases where it bites us. Let's look at a modified version of your example first:
sub.MyProperty.Returns(someValue);
First, sub.MyProperty
is called, which returns an IMyObject
. The Returns
extension method is then called, which needs to somehow work out which call it needs to return someValue
for. To do this, NSubstitute records the last call it received in some global state somewhere. Returns
in pseudo-ish-code looks something like this:
public static void Returns<T>(this T t, T valueToReturn) {
var lastSubstitute = bigGlobOfStaticState.GetLastSubstituteCalled();
lastSubstitute.SetReturnValueForLastCall(valueToReturn);
bigGlobOfStaticState.ClearLastCall(); // have handled last call now, clear static state
}
So evaluating the entire call looks a bit like this:
sub.MyProperty // <-- last call is sub.MyProperty
.Returns(someValue) // <-- make sub.MyProperty return someValue and
// clear last call, as we have already set
// a result for it
Now let's see what happens when we call another substitute while trying to set the return value:
sub.MyProperty.Returns(MyMethod());
Again this evaluates sub.MyProperty
, then needs to evaluate Returns
. Before it can do that, it needs to evaluate the arguments to Returns
, which means running MyMethod()
. This evaluation looks more like this:
//Evaluated as:
sub.MyProperty // <- last call is to sub.MyProperty, as before
.Returns(
// Now evaluate arguments to Returns:
MyMethod()
var ob = Substitute.For<IMyObject>()
ob.Value // <- last call is now to ob.Value, not sub.MyProperty!
.Returns(1) // <- ok, ob.Value now returns 1, and we have used up the last call
//Now finish evaluating origin Returns:
GetLastSubstituteCalled *ugh, can't find one, crash!*
There is another example of the problems this can cause here.
You can work around this by deferring the call to MyMethod()
, by using:
sub.MyProperty.Returns(x => MyMethod());
This works because MyMethod()
will only execute when it needs to use a return value, so the static GetLastSubstituteCalled
method doesn't get confused.
Rather than doing that though, I prefer avoiding other calls to substitutes while I am busy configuring one.
Hope this helps. :)