带有 NSubstitute 的 LINQPad 中的奇怪行为
Strange behaviour in LINQPad with NSubstitute
我在 LINQPad 4 (v.4.57.02
) 中编写了以下示例来演示尝试使用 NSubstitute (v1.9.2.0
) 来模拟一个非虚拟类型的愚蠢行为 属性 :
void Main()
{
var foo = Substitute.For<Foo>();
foo.Alarm.Returns(2);
foo.Alarm.Dump();
}
public class Foo
{
public Foo()
{
Console.WriteLine("Foo ctor called.");
}
public virtual int Alarm
{
get; set;
}
}
此代码按预期工作并给出以下输出:
Foo ctor called.
2
现在,当我编辑代码以删除 Alarm
属性 上的 virtual
修饰符时,我希望看到一个 NSubstitute.Exceptions.CouldNotSetReturnDueToNoLastCallException
异常,其中包含智慧:
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.
然而,第一次我运行修改后的代码我得到:
Foo ctor called.
0
在随后的 运行 中,我得到了预期的异常。
现在我怀疑 LINQPad 管理 AppDomain 的方式以及 NSubstitute 的 Castle 代理的工作方式有一些有趣的事情发生 - 但我不知道是什么。举起手来,我只是没有时间深入研究这个问题,想知道是否还有其他人有明确的解释,因为了解 LINQPad 执行环境中的陷阱会让人感到欣慰。
如果您开始打开 LINQPad 的新实例并且 运行 没有 virtual
成员的代码,它将立即失败并出现预期的错误。
所以这是我对正在发生的事情的猜测。第一次代码是 运行 并且 virtual
成员 NSubstitute 的状态如下所示:
var foo = Substitute.For<Foo>();
foo.Alarm // 1. last call is foo.Alarm
.Returns(2); // 2. make foo.Alarm return `2`. Clear last call.
foo.Alarm // 3. last call is foo.Alarm
.Dump(); // 4. extension method -- doesn't clear last call
NSubstitute 静态存储对替代品的最后一次调用,因此它会一直挂起直到应用程序域消失。当您修改代码以再次删除 virtual
和 运行 时,步骤 2 中的 .Returns(2)
会找到上一个 运行 的步骤 3 中的最后一次调用,将其存根相应地,然后清除最后一次调用。由于非虚拟成员,没有进一步的调用被记录,因此后续 运行s 失败并出现预期错误。
我在 LINQPad 4 (v.4.57.02
) 中编写了以下示例来演示尝试使用 NSubstitute (v1.9.2.0
) 来模拟一个非虚拟类型的愚蠢行为 属性 :
void Main()
{
var foo = Substitute.For<Foo>();
foo.Alarm.Returns(2);
foo.Alarm.Dump();
}
public class Foo
{
public Foo()
{
Console.WriteLine("Foo ctor called.");
}
public virtual int Alarm
{
get; set;
}
}
此代码按预期工作并给出以下输出:
Foo ctor called.
2
现在,当我编辑代码以删除 Alarm
属性 上的 virtual
修饰符时,我希望看到一个 NSubstitute.Exceptions.CouldNotSetReturnDueToNoLastCallException
异常,其中包含智慧:
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.
然而,第一次我运行修改后的代码我得到:
Foo ctor called.
0
在随后的 运行 中,我得到了预期的异常。
现在我怀疑 LINQPad 管理 AppDomain 的方式以及 NSubstitute 的 Castle 代理的工作方式有一些有趣的事情发生 - 但我不知道是什么。举起手来,我只是没有时间深入研究这个问题,想知道是否还有其他人有明确的解释,因为了解 LINQPad 执行环境中的陷阱会让人感到欣慰。
如果您开始打开 LINQPad 的新实例并且 运行 没有 virtual
成员的代码,它将立即失败并出现预期的错误。
所以这是我对正在发生的事情的猜测。第一次代码是 运行 并且 virtual
成员 NSubstitute 的状态如下所示:
var foo = Substitute.For<Foo>();
foo.Alarm // 1. last call is foo.Alarm
.Returns(2); // 2. make foo.Alarm return `2`. Clear last call.
foo.Alarm // 3. last call is foo.Alarm
.Dump(); // 4. extension method -- doesn't clear last call
NSubstitute 静态存储对替代品的最后一次调用,因此它会一直挂起直到应用程序域消失。当您修改代码以再次删除 virtual
和 运行 时,步骤 2 中的 .Returns(2)
会找到上一个 运行 的步骤 3 中的最后一次调用,将其存根相应地,然后清除最后一次调用。由于非虚拟成员,没有进一步的调用被记录,因此后续 运行s 失败并出现预期错误。