代码之家  ›  专栏  ›  技术社区  ›  EF0

为什么FakeItEay抛出此异常,为什么使方法虚拟化会修复它?

  •  14
  • EF0  · 技术社区  · 10 年前

    我有一个测试(代码如下)来测试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 回复  |  直到 10 年前
        1
  •  25
  •   David    10 年前

    虽然通常应用于类范围, sealed 在这种情况下,指的是无法覆盖所讨论的方法。使用 sealed 只有当方法是 override -然而,最初不是虚拟的方法不能被覆盖,因此它们本身被隐式密封。

    此错误所指的是它不能接受非- virtual 方法,因为它正在动态创建从给定类继承的类来执行这些拦截。在这样的级别上,它既不能确定非虚拟方法和密封方法之间的区别,也不需要-它无法覆盖,因此无法插入适当的截距。

        2
  •  11
  •   Blair Conrad    10 年前

    阿拉沃的回答很好。我敦促你接受它和/或投票支持它。

    不过,还有另一种方法。制作 _service2 假的 IService2 .

    然后,您不必更改任何方法的签名(接口方法总是可重写/可截获的)。一般来说,与具体(甚至抽象)类相比,伪造接口接口更容易,并且它具有实际测试协作类可以与接口一起工作的良好效果,而不一定只与接口的特定实现一起工作。

    当我在这里的时候,这一部分与您的错误并没有太大的关系,但可能有助于使您的代码更加清晰,因为您正在测试 Service1 ,我不会假装;使用实际的 服务1 例子这让读者清楚地了解了实际测试的内容。伪造测试系统被广泛认为是一种代码气味。

    如果您碰巧同时采用了这两个点,您的设置将看起来更像这样:

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

    你的考试应该通过了。

        3
  •  0
  •   GDubz    9 年前

    我两天来一直在研究一个几乎相同的问题。布莱尔·康拉德(Blair Conrad)的上述在界面层面造假的解决方案对我很奏效,而且实际上也很有意义:

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