在上篇读书笔记中介绍了四种测试替身,分别是stub, fake object, test spy, mock object。 在介绍如何正确使用这四种不同类型的测试替身前,有必要结合我自己的理解来区分这四种不同的替身。
首先这四种类型里,最简单的实现是stub,stub只负责返回相同的值,且一般不关心输入是什么。最复杂的是fake object, fake object基本上是个原有对象的简单实现版本。mock object和test spy的复杂介于两者中间。test spy需要通过记录内部的状态来决定是否测试通过。mock是复杂一点的stub,可以根据特定输入产生特定输出,又带有test spy的功能,因为它可以记录传入的参数或者调用的次数,但除了这些mock工具库自动可以支持的记录外,一般不需要人工维护其他内部状态。
上面说得还是有点绕。有人可能觉得根本不需要区分吗,会用就行了,写的时候跟着感觉走,何必费力气跟着别人的分类走呢。当然我也觉得纠结于分类意思不大。这里要搞清楚的本质问题是你的替身在你的测试中所要起到的作用是什么。作者引用了JUnit Recipes的作者一句话
Stub queries;mock actions.
这句话点明了两种功能的测试替身,一种为测试代码提供数据,一种为测试代码提供交互行为。测试是否通过依赖于:1. 否有正确地处理了数据,2. 是否与依赖的代码产生过正确的交互行为。当然很多替身是两种功能都有,只是偏向的程度不同。不由想起一个著名的公式,程序=数据结构+算法。
下面作者介绍的是写一个test的简单规范,真的很简单,就是arrange,act,assert。arrange就是一开始把测试对象,各种mock对象和数据准备好,act触发行为,assert最后判断结果是否是想要的。从BDD的角度来看,叫做given, when, then。非常容易理解就不累述了。
作者在此强调了一点大原则(此书后面会具体再展开),就是在assert中要测试行为而不是实现。比如我们如果只在乎测试输入通过测试代码后的数据输出是否正确,assert就并不需要去断言代码是否调用过某个外部接口,因为这是实现的问题。
最后的问题就是如何注入你的测试替身了。对于java老代码,可能需要通过反射之类,或者为了测试需要加上一些暴露私有成员的get, set方法。这当然是迫不得已的办法。最好是在代码一开始就基于某种DI框架,比如Spring。
全书第一部分到此就结束了。主要介绍了好的测试的一些特点(可读性,可维护性,可靠性)以及如何用好测试替身。第二部分中作者通过反例,也就是分类罗列出test的code smell的形式来介绍如何写好的测试。好的代码都是相似的,不好的代码有各种不好,所以从反面通过挑问题的方式来介绍正确的做法,我觉得有道理,也比较实用。
每周一篇读书笔记的速度来介绍这本书感觉有点慢,以后可能需要一周两更。
网友评论