伪造被测试对象的方法

6

你是否有理由不创建对象的部分虚假或仅伪造正在测试的对象的一个方法,以便测试另一个方法?这可能有助于节省制作整个新模拟对象的时间,或者当您在伪造的方法中存在外部依赖关系时,您无法合理地摆脱它,并且想要将其排除在所有其他单元测试之外。


抱歉,只是为了澄清 - 您正在测试对象A,并且对象A依赖于对象B,因此您想创建对象B的部分虚拟? - Roy Truelove
阅读有关模拟和存根的更多信息。这些技术正是您所询问的内容。 http://en.wikipedia.org/wiki/Mock_object http://en.wikipedia.org/wiki/Test_stubs - vittore
你需要使用Powermock。使用它,你可以模拟静态方法、私有方法、构造函数,因为它会调整类的字节码,所以你可以做任何魔法。这正是你所需要的。 - Mr.Eddart
不,创建对象A的部分伪造。 - Keith Pinson
5个回答

3
你想要进行此操作的对象试图做太多事情。特别是,如果您有外部依赖项,通常会创建一个对象来隔离该依赖项。Façade模式就是其中的一个例子。如果您的对象没有考虑可测试性,您可能需要进行一些重构。请查看Michael Feathers关于处理遗留代码的PDF(PDF)。他还有一本同名书籍,深入介绍了这个主题。

2
将类的一部分进行模拟/伪造来测试另一部分是一个非常糟糕的想法。
这样做,您无法测试真实代码在测试条件下的表现,导致测试结果不可靠。
这也会增加类的伪造部分的维护负担。如果对整个测试程序都生效,则伪造实现也会使其他对该方法进行测试的测试变得更加困难。
您需要问自己为什么需要伪造正在测试的部分。
如果是因为该方法正在访问文件或数据库,则应定义一个接口并将该接口的实例传递给类的构造函数或方法。这允许您在同一测试应用程序中测试不同的场景。
如果是因为您正在使用单例模式,则应重新考虑设计以使其更易于测试:删除单例将删除隐式依赖和维护噩梦。
如果您正在使用静态方法/独立函数来访问注册表或设置文件中的数据,则应将其移出要进行测试的函数并将数据作为参数传递或提供设置提供程序接口。这将使代码更加灵活和健壮。
如果是为了打破依赖关系以进行测试(例如,伪造向量方法以测试矩阵类中的方法),则不应该这样伪造--您应该将要测试的代码视为由其公共接口定义的类的代码:方法;前提条件,后置条件,不变量,文档,参数和异常规范。
您可以利用实现细节的知识来测试特殊边缘情况,但是通过主API触发这些情况,而不是通过伪造实现细节。
例如,假设您伪造了std::vector::at(),但实现切换为使用operator[]。您的测试将失败或悄悄地通过。

1
如果你想要伪造的方法是虚拟的(即不是静态的也不是最终的),那么你可以在测试中对你的对象进行子类化,覆盖子类中的方法,并在测试中使用子类。不需要任何模拟对象库。
(理想情况下,你应该考虑重构,这不是一个很好的长期解决方案。但这是一种让遗留代码能够被测试的方法,这样你就可以更容易地开始重构过程。)

1
在罗伊·奥舍罗夫的《单元测试的艺术》第3章中描述的“提取和重写”技术似乎是一种伪造被测试类的部分的方法(第71-77页)。奥舍罗夫没有解决其他答案中提出的问题。
此外,迈克尔·菲瑟斯在《与遗留代码有效地工作》中讨论了这个问题。他将结果类称为“测试子类”(227),并将该技术称为“子类和覆盖方法”(401)。当然,菲瑟斯并没有介绍新代码推荐的原始技术。但他仍然认为这是一种有潜力的有用技术。
我还向我的计算机教授咨询了这个问题。他博览群书,目前在软件行业全职工作,并迅速晋升。他说这种技术绝对有很好的应用,他所在公司的代码库中有几十个类以这种方式进行测试。他说,像任何技术一样,它可能会被过度使用。

我最初写下这个问题时,对单元测试几乎一无所知,也不了解依赖注入。现在,经过一些经验的积累,我想补充说,使用这种测试技术的需要可能是一种不好的信号。它可能意味着您需要重新设计依赖项的方法。如果需要模拟的方法是继承自基类的,则可能意味着您需要更认真地考虑“组合优于继承”的格言。您应该注入您的依赖项而不是继承它们。


《Osherove》的第三章已经上线了(PDF格式)。 - Hugh Brackett

0

有一些非常好的软件包可以方便地完成这种工作。例如,来自Mockito文档

//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);

你会得到“first”,因为你在“when”语句中所说的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接