xUnit测试模式:测试双模式
本章的模式
- 测试双
- 测试双重使用
- 测试存根
- 测试的间谍
- 模拟对象
- 假的对象
- 试验双结构
- 可配置测试双
- 硬编码测试双
- 浪费子类
测试双
当逻辑所依赖的代码不可用时,我们如何独立地验证逻辑?
我们如何避免慢测试?
我们用“特定于测试的等价物”替换SUT所依赖的组件。
有时测试SUT非常困难,因为它依赖于不能在测试环境中使用的其他组件。出现这种情况可能是因为这些组件不可用,因为它们不会返回测试所需的结果,或者因为执行它们会产生不希望出现的副作用。在其他情况下,我们的测试策略要求我们对SUT的内部行为有更多的控制或可见性。
当我们在编写一个我们不能(或选择不)使用真正依赖组件(DOC)的测试时,我们可以用一个测试双。的测试双不必完全像真正的DOC;它只需要提供与实际DOC相同的API,以便SUT认为这是真的!
它是如何工作的
当电影的制片人想要拍摄一些有潜在风险或危险的事情,让主角执行时,他们会雇佣一个“特技替身”来代替演员在场景中表演。特技替身是一个训练有素的人,能够满足现场的特定要求。特技替身可能不会表演,但他或她知道如何从高处坠落,撞车,或做任何场景要求的事情。特技替身与演员的相似程度取决于场景的性质。通常情况下,事情可以这样安排,一个在身材上与演员模糊相似的人可以代替演员的位置。
出于测试目的,我们可以用我们的相当于“特技替身”的是测试双。在我们的夹具设置阶段四阶段测试,我们把真正的DOC换成我们的测试双。方法的行为可以硬编码,这取决于我们正在执行的测试的类型测试双或者我们可以在设置阶段配置它。当SUT与测试双,它不会意识到它不是在和真正的麦考伊说话,但我们将实现我们的目标,使不可能的测试成为可能。
不管是哪种变异测试双当我们选择使用时,我们必须记住,我们不需要实现DOC的整个接口。相反,我们只提供特定测试所需的功能。我们甚至可以建造不同的测试双打对于涉及相同DOC的不同测试。
何时使用它
我们可能需要使用某种测试双在我们的测试中,在以下情况下:
- 如果我们有未经测试的要求(见生产错误),因为SUT及其文档都没有为SUT的间接输出提供观测点,我们需要使用它进行验证行为验证
- 如果我们有未测试的代码(见生产错误),而DOC并没有提供一个控制点,让我们可以使用必要的间接输入来执行SUT
- 如果我们有缓慢的测试我们希望能够更快、更频繁地运行我们的测试
每个场景都可以通过使用测试双。当然,我们在使用的时候一定要小心测试双打因为我们在测试SUT时使用的配置与在生产环境中使用的配置不同。由于这个原因,我们确实应该至少有一个测试来验证SUT在没有测试双。我们需要小心不要替换我们试图验证的SUT部分,因为这种做法可能导致测试错误的软件!还有,过度使用测试双打会导致脆弱的测试作为…的结果Overspecified软件。
测试双打有几种主要的口味,如总结在图23.1。在相应的模式文章中更详细地描述了这些模式的实现变化。
图23.1测试双打的类型。虚拟对象实际上是值模式的替代方案。测试存根用于验证间接输入;测试间谍和模拟对象用于验证间接输出。伪对象提供了另一种实现。
这些变化是根据我们如何/为什么使用的测试双。我们会处理一些变化构建的测试双打在“实现”部分中。
变体:测试存根
我们用a测试存根替换SUT所依赖的真实组件,以便测试对SUT的间接输入有一个控制点。它的包含允许测试强制SUT沿着它可能不会执行的路径运行。我们可以进一步分类测试存根通过它们注入SUT的间接输入。一个应答器(见测试存根)注入有效值,而a破坏者(见测试存根)注入错误或异常。
有些人使用术语“测试存根”来表示仅在实际对象或过程可用之前使用的临时实现。我更愿意称这种用法为a临时测试存根(见测试存根)以避免混淆。
变体:Test Spy
我们可以用一个更有能力的测试存根,测试的间谍,作为SUT间接输出的观测点。就像一个测试存根,一个测试的间谍可能需要向SUT提供值以响应方法调用。的测试的间谍但是,它还捕获SUT在运行时的间接输出,并将它们保存下来晚些时候通过测试进行验证。因此,在许多方面,测试的间谍是" just a "测试存根有一些记录功能。而一个测试的间谍的基本用途与模拟对象,我们使用a编写的测试样式测试的间谍看起来更像是用测试存根。
变体:模拟对象
我们可以用a模拟对象作为观测点来验证SUT的间接输出作为它是经过锻炼的。通常,模拟对象的功能测试存根因为它必须返回值给SUT,如果它还没有通过测试,但empha姐姐[1]是对间接输出的验证。因此,一个模拟对象不仅仅是一个测试存根plus断言:它以一种完全不同的方式使用。
变体:假物体
我们用a假的对象为了验证SUT的间接输入和输出以外的原因,在测试中取代真实DOC的功能。通常,一个假的对象实现了与实际DOC相同的功能,但方式简单得多。而一个假的对象通常是专门为测试而构建的,测试不使用它作为控制点或观察点。
使用a的最常见原因假的对象真正的DOC还不可用,太慢,或者由于有害的副作用而不能在测试环境中使用。侧栏“没有共享fixture的更快测试”描述了我们如何将所有数据库访问封装在持久层接口之后,然后用内存中的散列表替换数据库,从而使我们的测试运行速度提高50倍。第六章,测试自动化策略,第11章,使用测试双打,提供各种可用于简化SUT测试的技术的概述。
变体:虚拟对象
SUT的某些方法签名可能需要对象作为参数。如果测试和SUT都不关心这些对象,我们可以选择传入a虚拟的对象,它可以像空对象引用、object类的实例或类的实例一样简单Pseudo-Object(见硬编码测试双)。在这个意义上,a虚拟的对象不是真的吗?测试双而是价值模式的替代品文字值,派生值,生成的值。
变体:过程测试存根
一个测试双用过程式编程语言实现的测试存根通常被称为“测试存根”,但我更喜欢称之为“测试存根”程序测试存根(见测试存根)来区分这种用法与现代用法测试存根的变化测试双打。通常,我们用a程序测试存根允许测试/调试在等待其他代码可用时继续进行。这些对象很少在运行时“交换”,但有时我们使代码以“调试”标志为条件-一种形式的生产中的测试逻辑。
实现注意事项
当我们建造时,必须考虑到几个因素测试双(图23.2):
- 是否测试双应该特定于单个测试还是跨多个测试重用
- 是否测试双应该存在于代码中还是在运行中生成
- 我们如何告诉SUT使用测试双(安装)
第一点和最后一点在这里讨论。的讨论测试双Generation留给了上一节可配置的测试双精度。
因为建筑技术测试双打是否完全独立于它们的行为(例如,它们适用于两者测试存根和模拟对象),我选择将我们可以构建的各种方式的描述分开硬编码测试双精度和可配置测试双组分成不同的模式。
图23.2带有实现选择的测试double类型。只有测试存根、测试间谍和模拟对象需要由测试进行硬编码或配置。虚拟对象没有实现;假对象被安装但不受测试控制。
变化:不可配置的测试双
既不虚拟的对象也不假的物品需要配置的,各有各的原因。接收方不应该使用假人,所以它们不需要“真正的”实现。假的物品相比之下,它们需要一个“真正的”实现,但要比它们所取代的对象更简单或“更轻”。因此,测试和测试自动化都不需要配置“固定的”响应或期望;我们只需要安装测试双让SUT把它当成真的来使用。
变体:硬编码测试Double
当我们计划使用特定的测试双在单个测试中,通常最简单的方法是硬编码测试双返回特定的值(为测试存根)或期望特定的方法调用(模拟对象)。硬编码测试双精度通常是由测试自动化人员手工制作的。它们有几种形式,包括自并励(见硬编码测试双),在哪里Testcase类作为测试双;的匿名内部测试双(见硬编码测试双),在哪里使用语言特性来创建测试双在测试方法;和测试双独立实现考试双班(见硬编码测试双)。每个选项都将在硬编码测试双。
变化:可配置测试双
当我们想用相同的测试双实现在许多测试中,我们通常更喜欢使用可配置测试双。尽管测试自动化器可以手动构建这些对象,但是xUnit家族的许多成员都有可用于生成的可重用工具包可配置测试双组。
安装测试双管
在我们可以使用SUT之前,我们必须告诉它使用测试双而不是对象测试双替换。我们可以用任何可替换的依赖的模式来安装测试双在我们的夹具设置阶段四阶段测试。可配置测试双组需要在运行SUT之前进行配置,并且我们通常在安装它们之前执行此配置。
示例:Test Double
因为有各种各样的原因使用的变化测试双打,很难提供一个单独的例子来描述每种风格背后的动机。请参考前面提到的每个更详细的模式中的示例。