可测试性系列之测试替身Test Double

2022-06-07 18:35:30 浏览数 (1)

在做程序测试时,常会用到测试替身来协助我们快速完成测试。

有时候被测试系统(system under test(SUT))很难测试,因为在测试环境下依赖的组件不能正常使用。如外部系统。

当在一个不能使用真实依赖组件(depended-on component(DOC))的地方写test时,我们可以使用Test Double[1]。

Test Double

Test Double概括起来,有以下几种:

Martin Fowler在Mocks Aren't Stubs [2]中给出解释:

Martin Fowler解释的还不是太明白,我又收集了更明白一点的解释[3]:

•Dummy - just bogus values to stisfy the API

Example: If you're testing a method of a class which requires many mandatory parameters in a constructor which have no effect on your test, then you may create dummy objects for the purpose of creating new instances of a class.

•Fake - create a test implementation of a class which may have a dependency on some external infrastructure. (It's good practice that your unit test does NOT actually interact with external infrastructure.)

Example: Create fake implementation for accessing a database, replace it with in-memory collection.

•Stub - override methods to return hard-coded values,also refered to as state-based

Example:Your test class depends on a method calculate() taking 5 minutes to complete.Rather than warit for 5 minutes you can replace its real implementation with stub that returns hard-coded values;taking only a small faction of the time.

•Mock - very similar to Stub But interaction-based rather than state-based.This means you don't expect from Mock to return some value,but to assume that specific order of method calls are made.

Example:You're testing a user registration class.After calling save,it should call send ConfirmationEmail.

上面的解释已经很明白了,简单总结一下:

Dummy:主要是用来填充参数,以构造需要测试的对象

Fake:简单模拟实现,如Dao,但只是空实现

Stub:相对fake多了点硬编码返回值,两者很相似

Mock:相对状态验证更多的是行为验证

阅读到这儿大概率已经明白了,也就fake与stub还有点模糊,要想明白更清晰的区别,需要先了解一下生命周期和验证方式:

1、生命周期

每个测试都是由四个依次执行的阶段:

初始化(SetUp)、执行测试(Exercise)、验证结果(Verify)和复原(Teardown)

2、验证方式

在验证阶段,通常有两种验证方式:状态验证与行为验证

状态验证:

代码语言:javascript复制
//肯定会使用的assert
assertEquals(expected,actual);

行为验证:

代码语言:javascript复制
//Mockito中的verify
verify(mock, times(3)).do();

徐昊老师有个生动的描述:

万恶淫为首,论迹不论心,论心世上少完人;不关心过程,只看结果。结果验证

百善孝为先,论心不论迹,论迹贫家无孝子;看重过程,不看重结果。行为推断

所以我们判断淫棍总比判断孝子准确


测试策略是要保证有效性的同时,尽可能降低测试成本。

fake、stub、spy、mock 以此排序,成本越来越低,同时有效性也越来越低。

spy与mock在使用Mockito时能明显感受到它们俩的区别,fake与stub的区别,从上面的定义看,很接近。

徐昊老师引入进程的视角来进一步区分:

跨进程边界是fake,进程之间的stub就是fake。

结合上面的解释明确多了,当使用数据库时,fake一个内存数据库。

以进程划分:

进程间替身:dummy fake spy

进程内替身:stub mock spy

以验证方式划分:

严格来说,我们状态验证使用fake stub,行为验证使用mock。

dummy和spy都不验证,dummy是不能验,spy是还没验。

spy可以按状态(比如回放)也可以按行为(比如对比)验证。

spy已经在行为验证和状态验证的边上。spy是记录调用,对调用加上验证就是mock

如果用记录来reply就是录播测试,比如你在两个系统间做了spy,把请求和结果播放出来,这样相当于用spy的数据做了stub。

把请求结果和目标结果做对比,实际相当于拿spy数据做了mock

spy本身只取数据不验证,但是正常的doc内部数据不可知,因而spy是一种替身技术,并不是验证技术。

spy is dumb mock

总结

测试策略是要保证有效性的同时,尽可能降低测试成本。因此Test Double是SUT中测试中不可或缺的,Test Double的形式有dummy fake stub spy mock,以进程维度与验证方式维度能更好地区分它们。

References

[1] Test Double: http://xunitpatterns.com/Test Double.html [2] Mocks Aren't Stubs : https://martinfowler.com/articles/mocksArentStubs.html [3] 收集了更明白一点的解释: https://code-examples.net/en/q/34c8d7

0 人点赞