你好,我是徐昊。从这节课开始我们来正式学习测试驱动开发(Test-Driven Development,TDD)。

通过前面四节课的演示,相信你对测试驱动开发已经有了一定的感性认识。测试作为整个流程的驱动力,无论是在开发还是重构过程中都起到了重要作用。我想你对这一点一定有了比较深刻的印象。那么从今天开始,我们就来学习如何编写测试。

测试的基本结构

无论使用什么样的测试框架,每个测试都由四个依次执行的阶段组成:初始化(SetUp)、执行测试(Exercise)、验证结果(Verify)和复原(Teardown)。如下图所示:

需要说明一下的是,测试上下文(Test Context)在很多文献中被称作测试夹具(Test Fixture)。

夹具是个隐喻,是木工或者其他制造过程中,用以固定待加工工件的器具(上图中,棕色部分看起来是不是“夹”住了待测系统)。当然,这种拿一个生僻概念来隐喻另一个生僻概念的操作,也是很迷了。我要不是因为做吉他学习了木工,也不明白为什么要叫Fixture。抛开这个隐喻,直接称作测试上下文其实就简单易懂多了。

再多说一句,如果使用Fit系自动化测试工具(Fit、Fitness,甚至concordion、selenium),Fixture则表示驱动待测系统的交互接口,也叫Driver。这也是为啥,Selenium后来改叫WebDriver的原因。

言归正传,这四个阶段的主要作用是:

接下来,看一看前四节课演示的测试中,这四步都是如何实现的:

需要说明的是,在参数解析这个例子中,待测系统是命令行参数解析的Java类库(Java Library),并不涉及数据库、消息中间件、三方服务等进程外组件,也没有进程内三方组件依赖(比如OSGi Runtime、Servlet容器),因而测试上下文相对简单。而对于存在进程外组件或进程内三方组件依赖的场景,测试上下文的设置将直接影响编写测试的难度,以及维护测试的成本

在测试的四个步骤中,验证结果是最核心的一步,也是最核心的技术。验证结果有两种方式:状态验证(State Verification)和行为验证(Behavior Verification)。

状态验证

状态验证是指在与待测系统交互后,通过比对测试上下文与待测系统的状态变化,判断待测系统是否满足需求的验证方式。在前四节课的演示中,全部测试都采用了状态验证的方式:

状态验证是一种黑盒验证,它将测试上下文与待测系统当作一个整体。当待测系统不存在内部状态,而通过作用于依赖组件(Depended On Component)达成功能的时候,我们会从依赖组件中获取状态,以验证待测系统。如下图所示:

让我们回忆一下开篇词中“测试应用”的例子。其中StudentRepository自身不具有内部状态,而是通过EntityManager操纵数据库来完成功能。如果以TDD的方式来构造对于StudentRepository的测试,就会是这样的:

状态验证的一个难点是复原测试上下文,消除因执行测试造成的状态累积。如果待测系统是类库,一般问题不大,重新构造新的对象实例就行了。

而对于测试环境中存在进程外组件的情况,问题就要复杂一些了。在这种情况下,增量状态验证(Delta Verification)是一种有效的手段:

当然,在这个例子里,在每一个测试复原时,都使用了“drop-and-create”来清除数据库中的数据,从而消除状态累积。但如果因为种种原因(比如测试数据量很大),使得每次清除测试数据都变得不现实时,就可以使用增量状态验证来降低状态累积的影响。

小结

今天我们介绍了测试的基本结构,这个结构也叫四阶段测试(Four-Phase Test)。四阶段测试作为测试的基本模型,存在于各种测试框架中。而对于四阶段不同的组织方式,也构成了不同的测试编写风格,比如RSpec、ScalaTest等框架提供的BDD风格。不过无论是哪种风格,都不影响我们按照四阶段来理解测试的执行。

在四阶段中,验证结果是最核心的一步。它主要有状态验证和行为验证两种方式,其中状态验证需要大量使用断言方法(Assertation Method)来判断状态。同样,每个框架都提供了大量的断言方法,请参看具体框架的文档。

虽然状态验证是TDD中最重要的验证方法,但在某些情况下,我们仍然需要使用另一种验证方式——行为验证。这个我们将在下节课进行详细讲解。

思考题

为什么TDD中主要使用状态验证来验证测试的结果?

编辑来信

TDD是一项技能,唯有动手实操、反复练习,才能有所小成。为了帮助你更快地进步,徐昊老师特发起了“TDD专栏首发·代码评点”活动。
 
在第一个实战项目结束后,我们会根据你提交的学习反馈,手动选出其中几位进行代码评点与解疑答惑。而评点的详细内容我们也将制成加餐,展示在专栏里,供其他同学学习与参考。
 
划重点!如果学完第1-10讲再写反馈,将会大大提高你入选的机会!另,此次收集时间截至4月3日零点。所以非常希望你能跟上我们的更新进度,多动手实操,并记录学习体会。
 
最后,希望我们都能好好学习,更上层楼!