伪造被测对象的方法

时间:2011-07-15 19:52:10

标签: unit-testing testing mocking

为了测试另一种方法,有没有理由为什么你不应该为你正在测试它的对象创建一个对象的部分伪造或假冒一个方法?这可能有助于您避免制作一个全新的模拟对象,或者当您伪造的方法中存在外部依赖关系时,您无法合理地摆脱它并且想要避开所有其他单元测试?

5 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:2)

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

这样做,您不会测试实际代码在测试条件下的作用,从而导致测试结果不可靠。

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

你需要问问自己为什么需要假装被测部件。

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

如果是因为你使用的是单身人士,你应该重新考虑你的设计,使其更具可测性:删除单例会删除隐含的依赖关系和维护噩梦。

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

如果是为了测试目的而破坏依赖(例如伪造一个向量方法来测试矩阵类中的方法)那么你不应该假装 - 你应该将测试中的代码视为什么由公共接口测试的类定义:方法;前提条件,后置条件,不变量,文档,参数和异常规范。

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

例如,假设您伪造了std :: vector :: at(),但实现切换为使用operator []。你的测试会破裂或默默地通过。

答案 2 :(得分:1)

如果您想要伪造的方法是虚拟的(不是静态的而不是最终的),那么您可以在测试中继承您的对象,覆盖子类中的方法,并在测试中练习子类。不需要模拟对象库。

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

答案 3 :(得分:1)

Roy Osherove的单元测试艺术第3章中描述的提取和覆盖技术似乎确实是伪造被测课程的一部分的方法(pp .71-77)。 Osherove没有解决在这个问题的其他一些答案中提出的问题。

此外,Michael Feathers在有效使用遗留代码中讨论了这一点。他将结果类称为测试子类(227)和技术子类和覆盖方法(401)。现在,Feathers没有透露新代码推荐的原始技术。但他仍然认真对待它作为一种潜在有用的技术。

我还问过我的前电脑教授。他读得很好,目前在软件行业全职工作,他在那里迅速发展。他说这种技术肯定有很好的应用,并且他的公司代码库中有几十个类正在以这种方式进行测试。他说,就像任何技术一样,它可能被过度使用。

当我刚接触单元测试时,我最初写了这个问题,并且对依赖注入几乎一无所知。现在,经过两者的一些经验,我想补充一点,使用这种测试技术的需要可能是一种气味。这可能是需要重新设计依赖关系方法的标志。如果需要伪造的方法是从基类继承的方法,那么可能意味着你需要更严肃地对待格言“赞成组合而不是继承”。您应该注入依赖项而不是继承它们。

答案 4 :(得分:0)

有一些非常好的软件包可以促进这种东西。例如,来自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);

你会“第一次”回来,因为你在“何时”陈述中所说的话。