分布式程序的自动化回归测试

陈硕 (giantchen_AT_gmail)

Blog.csdn.net/Solstice  t.sina.com.cn/giantchen

 

陈硕关于分布式系统的系列文章:http://blog.csdn.net/Solstice/category/802325.aspx

 

本作品采用“Creative Commons 署名-非商业性使用-禁止演绎 3.0 Unported 许可协议(cc by-nc-nd)”进行许可。
http://creativecommons.org/licenses/by-nc-nd/3.0/

 

本文所谈的“测试”全部指的是“开发者测试/developer testing”,由程序员自己来做,不是由 QA 团队进行的系统测试。这两种测试各有各的用途,不能相互替代。

我在《朴实的C++设计》一文中谈到“为了确保正确性,我们另外用Java写了一个测试夹具(test harness)来测试我们这个C++程序。这个测试夹具模拟了所有与我们这个C++程序打交道的其他程序,能够测试各种正常或异常的情况。基本上任何代码改动和bug修复都在这个夹具中有体现。如果要新加一个功能,会有对应的测试用例来验证其行为。如果发现了一个bug,先往夹具里加一个或几个能复现bug的测试用例,然后修复代码,让测试通过。我们积累了几百个测试用例,这些用例表示了我们对程序行为的预期,是一份可以运行的文档。每次代码改动提交之前,我们都会执行一遍测试,以防低级错误发生。

今天把 test harness 这个做法仔细说一说。

自动化测试的必要性

我想自动化测试的必要性无需赘言,自动化测试是 absolutely good stuff。

基本上,要是没有自动化的测试,我是不敢改产品代码的(“改”包括添加新功能和重构)。自动化测试的作用是把程序已经实现的 features 以 test case 的形式固化下来,将来任何代码改动如果破坏了现有的功能需求就会触发测试 failure。好比 DNA 双链的互补关系,这种互补结构对保持生物遗传的稳定有重要作用。类似的,自动化测试与被测程序的互补结构对保持系统的功能稳定有重要作用。

单元测试的能与不能

一提到自动化测试,我猜很多人想到的是单元测试(unit testing)。单元测试确实有很大的用处,对于解决某一类型的问题很有帮助。粗略地说,单元测试主要用于测试一个函数、一个 class 或者相关的几个 classes。

最典型的是测试纯函数,比如计算个人所得税的函数,输出是“起征点、扣除五险一金之后的应纳税所得额、税率表”,输出是应该缴的个税。又比如我在《〈程序中的日期与时间〉第一章 日期计算》中用单元测试来验证 Julian day number 算法的正确性。再比如我在《“过家家”版的移动离线计费系统实现》和《模拟银行窗口排队叫号系统的运作》中用单元测试来检查程序运行的结果是否符合预期。(最后这个或许不是严格意义上的单元测试,更像是验收测试。)

为了能用单元测试,主代码有时候需要做一些改动。这对 Java 通常不构成问题(反正都编译成 jar 文件,在运行的时候指定 entry point)。对于 C++,一个程序只能有一个 main() 入口点,要采用单元测试的话,需要把功能代码(被测对象)做成一个 library,然后让单元测试代码(包含 main() 函数)link 到这个 library 上;当然,为了正常启动程序,我们还需要写一个普通的 main(),并 link 到这个 library 上。

单元测试的缺点

根据我的个人经验,我发现单元测试有以下缺点。

  • 阻碍大型重构

单元测试是白盒测试,测试代码直接调用被测代码,测试代码与被测代码紧耦合。从理论上说,“测试”应该只关心被测代码实现的功能,不用管它是如何实现的(包括它提供什么样的函数调用接口)。比方说,以前面的个税计算器函数为例,作为使用者,我们只关心它算的结果是否正确。但是,如果要写单元测试,测试代码必须调用被测代码,那么测试代码必须要知道个税计算器的 package、class、method name、parameter list、return type 等等信息,还要知道如何构造这个 class。以上任何一点改动都会造成测试失败(编译就不通过)。

在添加新功能的时候,我们常会重构已有的代码,在保持原有功能的情况下让代码的“形状”更适合实现新的需求。一旦修改原有的代码,单元测试就可能编译不过:比如给成员函数或构造函数添加一个参数,或者把成员函数从一个 class 移到另一个 class。对于 Java,这个问题还比较好解决,因为 IDE 的重构功能很强,能自动找到 references,并修改之。

对于 C++,这个问题更为严重,因为一改功能代码的接口,单元测试就编译不过了,而 C++ 通常没有自动重构工具(语法太复杂,语意太微妙)可以帮我们,都得手动来。要么每改动一点功能代码就修复单元测试,让编译通过;要么留着单元测试编译不通过,先把功能代码改成我们想要的样子,再来统一修复单元测试。

这两种做法都

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值