2016-02-11 141 views
1

我写了下面的示例中LINQPad 4(v.4.57.02)证明试图用NSubstitute(v1.9.2.0)嘲笑类型的愚蠢和它的非虚拟财产:奇怪的行为与NSubstitute

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包括智慧在内的例外情况:

如果您替换了某个类而不是某个接口,请检查对替代者的调用是否在虚拟/抽象成员上。无法为非虚拟/非抽象成员配置返回值。

然而,第一一次运行修改后的代码我得到:

Foo ctor called. 
0 

而且在随后的运行中,我得到了预期的异常。

现在我怀疑LINQPad管理AppDomain的方式以及NSubstitute的城堡代理如何工作 - 但我不知道是什么。举起手来,我只是没有时间深入研究,并想知道是否有其他人有明确的解释,因为知道LINQPad执行环境中的陷阱会令人欣慰。

回答

2

如果您开始打开一个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店,所以直到AppDomain中消失它徘徊。当您修改代码以删除virtual并再次运行时,步骤2中的.Returns(2)将查找上一次运行的第3步中进行的最后一次呼叫,并相应地对其进行存根,然后清除最后一次呼叫。由于非虚拟成员,未记录其他呼叫,因此后续运行失败并出现预期错误。

+0

这当然有很大的意义。好一个。 –

+1

此外,重要的是要注意来自上一次运行的调用是在存根对象的另一个实例上,这意味着'.Returns(2)'调用是在不再可从LINQPad代码访问的对象上进行的,但是可以访问来自NSubstitute中“最后呼叫”概念的静态存储。 –