为什么FakeItEasy会抛出此异常,为什么要使方法虚拟修复呢?

EF0*_*EF0 11 c# testing asp.net-mvc fakeiteasy

我有一个测试(代码如下)来测试Method1调用Method2.我得到的例外是

当前代理生成器无法拦截指定的方法,原因如下: - 无法拦截密封方法.

被测方法本身并未密封.但是,它确实依赖于密封类(第三方类,我在创建包装器时遇到了麻烦,以便正确地模拟它 - 另一个问题的另一个主题).无论哪种方式,在这一点上,我并不是要求FakeItEasy嘲笑密封的班级.在调试我的测试时,当调用依赖项时,我可以清楚地看到正在生成一个真实的对象,而不是假的.

然而,鉴于错误信息,我觉得它可能会以某种方式相关.

此外,我通过随机博客文章发现,使方法虚拟修复问题,允许测试通过.我试了一下它就有效了.但我不明白为什么它修复它,而且无论如何,保持方法虚拟对我没有意义.就我而言,被测试的班级没有自己的孩子,即; 没有孩子覆盖它的方法,所以我看不出有任何理由让它成为虚拟的.

我错误地认为我没有理由让这个方法成为虚拟的吗?
是FakeItEasy以某种方式试图嘲笑密封的班级?

我真的不确定如何进行这项测试.

我的测试

[SetUp]
public void SetUp()
{
    // Arrange
    _service2 = A.Fake<Service2>(x => x.WithArgumentsForConstructor(
                                        () => new Service2()));
    _service1 = A.Fake<Service1>(x => x.WithArgumentsForConstructor(
                                        () => new Service1(_service2)));
}

[Test]
public void when_Method1_executes_it_calls_Method2()
{
    // Act
    result = _service1.Method1();

    // Assert
     A.CallTo(() => _service2.Method2())
                                   .WithAnyArguments()
                                   .MustHaveHappened();
}
Run Code Online (Sandbox Code Playgroud)


相关方法

public class Service1 : IService1
{
    private readonly IService2 _service2;
    public Service1(IService2 service2)
    { 
        _service2 = service2; 
    }

    public bool Method1()
    {
        using (var dependency = new MyDependency()) // third party sealed class
        {
        }

        var x = _service2.Method2();            
    }
}   


public class Service2 : IService2
{
    public bool Method2() // making this virtual fixes the FakeItEasy exception
    {            
    }
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*vid 23

虽然通常应用于类范围,但在这种情况下密封是指无法覆盖相关方法.使用sealed方法仅在方法是有效时才有效override- 但是,首先不是虚拟的方法不能被覆盖,因此它们本身是隐式密封的.

这个错误引用的是它不能接受非方法,因为它正在创建一个从给定类继承的动态类来执行这些拦截.在这样的水平上,它既不能确定非关键方法和密封方法之间的差异,也不能确定 - 它不能覆盖,因此不能插入适当的拦截.

  • 很好的答案.但有一点澄清:[密封](http://msdn.microsoft.com/en-us/library/88c54tsw.aspx)对方法有效.它具有取消可能已应用于父方法的任何虚拟的效果.正如你所说,FakeItEasy使用Castle Dynamic Proxy创建一个继承自"Service2"的类.当执行`A.CallTo`时,它发现该方法无法被覆盖,因此FakeItEasy无法拦截这些调用. (3认同)

Bla*_*rad 11

阿拉沃尔的答案很棒.我敦促你接受和/或投票.

不过还有另一种方法.做_service2一个假的IService2.

然后您不必更改任何方法的签名(接口方法总是可覆盖/可拦截).通常,伪造接口接口比具体(甚至是抽象)类更容易,并且它具有实际测试的良好效果,即您的协作类可以使用接口,而不仅仅是接口的特定实现.

虽然我在这里,但这部分并不是与你的错误有关,但可能有助于使你的代码更清晰,因为你正在测试Service1,我不会假装它; 使用实际的Service1实例.这使读者清楚地了解了实际测试的内容.伪装被测系统被广泛认为是代码气味.

如果您碰巧得到这两点,您的SetUp看起来会更像这样:

[SetUp]
public void SetUp()
{
    // Arrange
    _service2 = A.Fake<IService2>();
    _service1 = new Service1(_service2);
}
Run Code Online (Sandbox Code Playgroud)

你的测试应该通过.