[英]Returning the result of a method that returns another substitute throws an exception in NSubstitute
几次使用NSubstitute时遇到了一个奇怪的问题,尽管我知道如何解决该问题,但我始终无法解释。
我精心设计了似乎是证明该问题所需的最低限度测试,它似乎与使用创建替代返回值的方法有关。
public interface IMyObject
{
int Value { get; }
}
public interface IMyInterface
{
IMyObject MyProperty { get; }
}
[TestMethod]
public void NSubstitute_ReturnsFromMethod_Test()
{
var sub = Substitute.For<IMyInterface>();
sub.MyProperty.Returns(MyMethod());
}
private IMyObject MyMethod()
{
var ob = Substitute.For<IMyObject>();
ob.Value.Returns(1);
return ob;
}
运行上述测试时,出现以下异常:
Test method globalroam.Model.NEM.Test.ViewModel.DelayedAction_Test.NSubstitute_ReturnsFromMethod_Test threw exception:
NSubstitute.Exceptions.CouldNotSetReturnException: Could not find a call to return from.
Make sure you called Returns() after calling your substitute (for example: mySub.SomeMethod().Returns(value)).
If you substituted for a class rather than an interface, check that the call to your substitute was on a virtual/abstract member.
Return values cannot be configured for non-virtual/non-abstract members.
但是,如果我更改测试方法以返回此值:
sub.MyProperty.Returns((a) => MyMethod());
或这个:
var result = MyMethod();
sub.MyProperty.Returns(result);
有用。
我只是想知道是否有人可以解释为什么会这样?
为了使NSubstitute语法起作用,幕后发生了一些混乱。 这是它咬我们的那些情况之一。 首先让我们看一下示例的修改版本:
sub.MyProperty.Returns(someValue);
首先, sub.MyProperty
,它返回一个IMyObject
。 然后调用Returns
扩展方法,该方法需要以某种方式计算出需要为其返回someValue
调用。 为此,NSubstitute将在某个全局状态下某处收到的最后一个呼叫记录下来。 以伪ish-code 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()
是因为MyMethod()
仅在需要使用返回值时才执行,因此静态GetLastSubstituteCalled
方法不会引起混淆。
但是,我宁愿不这样做,而是在忙于配置替代项时避免其他调用。
希望这可以帮助。 :)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.