术语 | 解释 |
---|---|
SUT(System Under Test) | 被测系统 |
DOC(depended-on component) | 第三方依赖组件 |
简介&理解
TestDouble 简单理解就是测试替身,在多数情况下,我们的系统能够正常运行,不仅仅依托系统本身,还需要依赖一些外部服务,比如其他系统提供的 http、rpc 服务,系统自身以来的像 redis 缓存服务或者 mysql 这类数据库服务。在微服务场景下,业务按照业务领域将一个系统拆分为多个系统,系统之间的交互不仅仅是简单的 A->B,可能是 A ->B -> C ->D,对于编写单元测试的开发者来说,当我需要编写系统A 的测试用例时,不可能去构建完整的调用链路,那么在测试工程中,通常会以 “测试替身” 来解决外部依赖所带来的测试复杂性问题。
在进行单元测试时,使用 Test Double 最主要的目的就是减少被测试对象的依赖,使得测试更加单一,只需要关注在被测系统本身的一些测试场景;除此之外, Test Double 从某种角度来说,可以让测试案例执行的测试时间更短,运行也更稳定(替代了真实的外部依赖)。
Test Double 和实际交付使用的实际对象还是存在本质差别的,所以在实际的测试过程中,不建议 Test Double 的过度使用,因为可能会造成测试场景和实际场景脱节。
测试替身类型
测试替身主要包括以下几种类型:
- Dummy Object
- Test Stub
- Test Spy
- Mock Object
- Fake Object
Dummy Object
虚拟对象,本质上不会对测试产生任何影响,实际上只作为类似参数填充类角色存在。
Test Stub
测试桩是用来接受SUT内部的间接输入(indirect inputs),并返回特定的值给SUT。可以理解 Test Stub 是在SUT 内部打的一个桩,可以按照我们的要求返回特定的内容给 SUT,Test Stub 的交互完全在 SUT 内部,因此,它不会返回内容给测试案例,也不会对 SUT 内部的输入进行验证。
Test Stub 是指一个完全代替待测系统依赖组件的对象,这个对象按照我们设计的输出与待测系统进行交互,可以理解是在待测系统内部打的一个桩。这个桩既不会与测试用例(代码)交互,也不会在待测系统内部进行验证。Test Stub常用于响应待测系统的请求,然后返回特定的值。接下来,这个值会对待测系统产生影响,然后我们就在测试用例里面去验证这个影响。
Test Stub的实现方式一般有两种:
- Hard-Coded Test Stub – 会返回固定 response 的 Test Stub
- Configurable Test Stub – 会根据测试需求返回相应 response 的 Test Stub,可配置化
当我们遇到下面场景时,Test Stub就可以派上用场
- 依赖组件无法使用,影响测试结果
- 依赖组件运行太慢,影响测试速度
- 成为Responder响应者,当需要给待测系统注入特定数据,从而对待测系统产生影响
- 成为Saboteur破坏者,当需要给待测系统注入无效数据,从而对待测系统产生异常影响,观察待测系统如何处理错误情况
Test Spy
Test Spy像一个间谍,安插在了 SUT 内部,专门负责将 SUT 内部的间接输出(indirect outputs)传到外部。它的特点是将内部的间接输出返回给测试案例,由测试案例进行验证,Test Spy 只负责获取内部情报,并把情报发出去,不负责验证情报的正确性。
Test Spy 是指一个待测系统依赖组件的替身,并且会捕捉和保存待测对象对依赖系统的输出,这个输出会用于测试代码中的验证。Test Spy 主要用于记录和验证待测对象对依赖系统的输出。
Test Spy 是把待测对象对依赖系统的输出拿到了测试代码里面进行验证,这样的话,如果 SUT 的输出不符合期望,Test Spy 并不像 Mock Object 那样第一时间让测试失败,而是可以在测试代码中加入更多判断信息,让验证和测试结果更加可控和可视化。
Mock Object
Mock Object 和 Test Spy 有类似的地方,它也是安插在 SUT 内部,获取到 SUT 内部的间接输出(indirect outputs),不同的是,Mock Object 还负责对情报(indirect outputs)进行验证,总部(外部的测试案例)信任 Mock Object 的验证结果。
Mock 更像是行为既定,通过 mock 对象预期输入产生预期输出。
Mock Object 一个重要的特点是它可以对无法在待测系统上直接被观察到的行为或输出进行验证。无法观察到的系统行为或输出可以是数据插入数据库,可以是数据写入文件,也可以是对其他组件的调用。以数据库类型 Mock Object 举例,这个 Mock 的数据库会去接受待测系统发过来的数据,并且对这个数据进行验证,一旦验证通过就会对数据进行处理(插入或更新操作),然后测试代码会去验证插入是否成功。
Fake Object
Fake Object 并不关注 SUT 内部的间接输入(indirect inputs)或间接输出(indirect outputs),它仅仅是用来替代一个实际的对象,并且拥有几乎和实际对象一样的功能,保证 SUT 能够正常工作。实际对象过分依赖外部环境,Fake Object 可以减少这样的依赖。这也是 Fake 和 Test Stub 最主要的区别。
简单说就是采用更加简单的方法实现依赖组件的功能,典型的例子就是使用 H2 来代替 Mysql 的测试。