Ответ 1
Чтобы заставить синтаксис NSubstitute работать, происходит некоторая беспорядочность, происходящая за кулисами. Это один из тех случаев, когда он нас кусает. Сначала рассмотрим измененную версию вашего примера:
sub.MyProperty.Returns(someValue);
Сначала вызывается sub.MyProperty
, который возвращает IMyObject
. Затем вызывается метод расширения Returns
, который должен каким-то образом решить, какой вызов ему нужно вернуть someValue
для. Для этого NSubstitute записывает последний вызов, который он получил в каком-либо глобальном состоянии. Returns
в псевдоиш-коде выглядит примерно так:
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
}
Итак, оценка всего вызова выглядит примерно так:
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
Теперь посмотрим, что произойдет, когда мы вызываем другую замену при попытке установить возвращаемое значение:
sub.MyProperty.Returns(MyMethod());
Снова это оценивает sub.MyProperty
, тогда необходимо оценить Returns
. Прежде чем он сможет это сделать, ему необходимо оценить аргументы Returns
, что означает запуск MyMethod()
. Эта оценка выглядит следующим образом:
//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!*
Есть еще один пример проблем, которые могут вызвать здесь.
Вы можете обойти это, отложив вызов до MyMethod()
, используя:
sub.MyProperty.Returns(x => MyMethod());
Это работает, потому что MyMethod()
будет выполняться только тогда, когда ему нужно использовать возвращаемое значение, поэтому статический метод GetLastSubstituteCalled
не запутается.
Вместо того, чтобы делать это, я предпочитаю избегать других вызовов заменителей, пока я занят настройкой.
Надеюсь, это поможет.:)