【问题标题】:Faking a Method of the Object Under Test伪造被测对象的方法
【发布时间】:2011-07-15 19:52:10
【问题描述】:

您是否有理由不创建一个对象的部分假冒,或者只是为了测试另一种方法而在您正在测试的对象上伪造一个方法?这可能有助于避免您制作一个全新的模拟对象,或者当您伪造的方法中存在外部依赖项时,您无法合理地摆脱它并希望避开所有其他单元测试?

【问题讨论】:

  • 抱歉,澄清一下 - 您有对象 A 正在测试中,而对象 A 依赖于对象 B,所以您想创建对象 B 的部分假冒?
  • 阅读更多关于模型和存根的信息。这些技术正是你要问的en.wikipedia.org/wiki/Mock_objecten.wikipedia.org/wiki/Test_stubs
  • 你需要Powermock。有了它,你可以模拟静态方法、私有方法、构造函数,你真的可以做任何魔法,因为它调整了类的字节码。这正是您所需要的。
  • 不,创建对象 A 的部分伪造。

标签: unit-testing testing mocking


【解决方案1】:

您要为其执行此操作的对象正在尝试执行太多操作。特别是,如果您有一个外部依赖项,您通常会创建一个对象来隔离该依赖项。 Façade 模式就是一个例子。如果您的对象在设计时没有考虑到可测试性,您可能需要进行一些重构。看看Michael Feathers' PDF on working with legacy code(PDF)。他还有一本同名的书,内容更详细。

【讨论】:

    【解决方案2】:

    模拟/伪造一个类的一部分来测试另一个类是一个非常糟糕的主意。

    这样做,您并没有测试真实代码在被测条件下的作用,从而导致不可靠的测试结果。

    这也增加了类的伪造部分的维护负担。如果这对整个测试程序都有效,那么伪造的实现也会使对伪造方法的其他测试更加困难。

    您需要问自己为什么需要伪造被测部件。

    如果是因为该方法正在访问文件或数据库,那么您应该定义一个接口并将该接口的实例传递给类构造函数或方法。这允许您在同一个测试应用程序中测试不同的场景。

    如果是因为您使用的是单例,您应该重新考虑您的设计以使其更可测试:删除单例将消除隐式依赖和维护噩梦。

    如果您使用静态方法/独立函数来访问注册表或设置文件中的数据,您应该真正将其移出被测函数并将数据作为参数传递或提供设置提供程序接口。这将使代码更加灵活和健壮。

    如果是为了测试目的而打破依赖关系(例如,伪造一个向量方法来测试矩阵类中的一个方法),那么你不应该伪装——你应该把被测代码当作是什么由被测类通过其公共接口定义:方法;前置条件、后置条件、不变量、文档、参数和异常规范。

    您可以使用实现细节的知识来测试特殊的边缘情况,但通过主 API 触发这些情况,而不是通过伪造实现细节。

    例如,假设您伪造了 std::vector::at() 但实现改为使用 operator[]。您的测试会中断或默默通过。

    【讨论】:

      【解决方案3】:

      如果您要伪造的方法是虚拟的(例如,不是静态的,也不是最终的),那么您可以在测试中子类化您的对象,覆盖子类中的方法,并在测试中执行子类。不需要模拟对象库。

      (理想情况下,您应该考虑重构,这不是一个很好的长期解决方案。但它是一种测试遗留代码的方法,因此您可以更轻松地开始重构过程。)

      【讨论】:

        【解决方案4】:

        Roy Osherove 的单元测试的艺术第 3 章中描述的 Extract and Override 技术似乎确实是一种伪造部分被测类的方法(pp . 71-77)。 Osherove 没有解决该问题的其他一些答案中提出的问题。

        此外,Michael Feathers 在有效地使用遗留代码中讨论了这一点。他将生成的类称为测试子类(227)和技术子类和覆盖方法(401)。现在,当然,Feathers 并没有说明推荐用于新代码的原始技术。但他仍然认真对待它,认为这是一种可能有用的技术。

        我还向我以前的计算机教授询问过这个问题。他博学多才,目前在软件行业全职工作,并在该行业发展迅速。他说这种技术肯定有很好的应用,他公司的代码库中有几十个类在用这种方式进行测试。他说,就像任何技术一样,它可能会被过度使用。

        我最初是在我刚接触单元测试并且对依赖注入几乎一无所知时写的。现在,在对两者都有一些经验之后,我要补充一点,使用这种测试技术的需要可能是一种气味。这可能是一个迹象,需要重新设计依赖关系的方法。如果需要伪造的方法是从基类继承的方法,则可能意味着您需要更认真地对待“优先组合胜过继承”这句格言。你应该注入你的依赖而不是继承它们。

        【讨论】:

        【解决方案5】:

        有一些非常好的软件包可以促进这类事情。比如来自Mockito docs

        //You can mock concrete classes, not only interfaces
        LinkedList mockedList = mock(LinkedList.class);
        
        //stubbing
        when(mockedList.get(0)).thenReturn("first");
        

        做了一些一开始很难相信的真正魔法。当你打电话时

        String firstMember = mockedList.get(0);
        

        由于您在“何时”声明中所说的话,您将“首先”返回。

        【讨论】:

          猜你喜欢
          • 2022-06-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多