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

时间:2014-07-11 16:41:18

标签: c# asp.net-mvc testing fakeiteasy

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

  

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

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

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

此外,我通过random blog post发现,使方法虚拟修复问题,允许测试通过。我试了一下它就有效了。但是我没有为什么它修复了它,而且无论如何,将方法保持为虚拟对我来说没有意义。就我而言,被测试的班级没有自己的孩子,即;没有孩子覆盖它的方法,所以我看不出有任何理由让它成为虚拟的。

我认为我没有理由让这个方法变得虚拟吗? 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();
}


相关方法

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
    {            
    }
}

3 个答案:

答案 0 :(得分:21)

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

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

答案 1 :(得分:11)

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

但是还有另一种方法。将_service2假冒IService2

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

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

如果您碰巧同时使用这两点,您的SetUp看起来会更像这样:

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

你的测试应该通过。

答案 2 :(得分:0)

我刚刚在两天内追逐几乎完全相同的问题。布莱尔康拉德在界面层面伪装的解决方案对我有用,而且实际上也很有意义:

如果被测试的类没有依赖于另一个类,那么测试也不应该。因此,您伪造了接口,而不是实现接口的类。