关键词: 白盒测试 第4代 测试方法 4GWM 在线测试 持续测试 灰盒 脚本驱动 脚本桩
摘 要: 本文是第4代白盒测试方法的理论介绍,描述3个关键领域内9项关键特征的概念与固有特征。同时介绍白盒测试发展历程,对比说明第4代白盒测试方法与以往测试方法的异同及优化要素。
缩略语:
4GWM:The 4th Generation White-Box-testing Methodology,第4代白盒测试方法
XP:Extreme Programming,极限编程
TDD:Test Driven Development,测试驱动开发
IID:Incremental and Iterative Development,渐增迭代开发
CSE:Common Script Engine,通用脚本引擎(一种近似于python的脚本语言)
PCO:Points of Control and Observation,观察控制点
TDF:Test Design First,测试设计先行
MCDC:Modified Condition/Decision Coverage
1背景
1.1白盒测试的范围
白盒测试是软件测试体系中一个分支,测试关注对象是一行行可见代码,如果代码不可见就不是白盒,是黑盒测试了。白盒测试也通常被认为是单元测试与集成测试的统称,但这个概念是相对的,与当前项目遵循的研发流程有关,某些流程把白盒测试划分为单元测试与集成测试,而另一些流程,把白盒测试划分为模块单元测试、模块系统测试、多模块集成测试,还有一些流程把单元测试与集成测试混为一体,统称为持续集成测试。
随着测试技术的发展,白盒测试的概念也在发生变化,比如,本文提倡一种介于白盒与黑盒之间的灰盒操作模式,针对被测对象同样是可见源码,这时,白盒测试不只是白盒了。尽管如果此,我们仍遵循大家习惯的思维方式——把本文倡导的测试方法仍冠名为:第4代白盒测试方法(4GWM,The 4th Generation White-Box-testing Methodology)。
本文讨论白盒测试方法,范围限定在功能测试之前,针对源码行的所有测试,即,被测对象是看得到的功能源码,每个测试者必须先获得源码才能实施测试。
1.2第1代与第2代白盒测试
说到第4代白盒测试方法,就不能不回顾前几代方法。在测试发展初期,测试工具很不成熟,人们通常以单步调试代替测试,或采用assert断言、print语句等简单方式的组织测试体系,即我们所谓的第1代白盒测试,这一时期的测试是半手工的,没实现自动化,测试效果也严重依赖测试者(或者调试者)的个人能力,缺少统一规范的评判标准。
当然,调试算不算测试在业界尚存争议,单论调试的目的(为了定位问题)与操作方式(过程不可重复),不应把调试看作测试,但调试确能发现软件BUG,显然这也是一种测试手段。本文暂不评判调试用作测试手段是否合理,但有必要先确定调试是测试的某种形式,把它看作特定历史阶段或特定场景下的产物。特定历史阶段大家比较容易理解,调试伴随编程语言是天生的,测试工具却是后天形成,开发人员总喜欢认调试器当亲妈,测试工具则是爱管不管的后妈。特定场景是什么?比如,某种生僻的RTOS平台根本找不到对应测试工具,怎么办?拿调试做测试是无奈之中的必然。这里,我们不否认调试也是一种测试,在此基础上再优化其操作过程,使调试能更好的服务于测试(下文介绍“灰盒调测”还有进一步论述)。
第1代白盒测试方法存在严重缺陷,主要有:测试过程难以重用,成功经验无法拷贝,测试结果也难以评估并用于改进,这些对于团队运作是非常致命的。
到第2代白盒测试,上述主要缺陷得到克服,将测试操作改用一种形式化语言(通常称为测试脚本)来表述,脚本可以组合成用例,用例可组合成测试集,用例与测试集再统一到测试工程中管理,把测试脚本保存到文件,重用问题解决了。另外,代码覆盖率功能使测试结果可以评估,能直观的看到哪些代码或分支未被覆盖,然后有针对性的增加测试设计。目前市面上有大量商用工具,如RTRT、CodeTest、Visual Tester、C++ Tester等都属于这第2代白盒测试工具。
1.3第3代白盒测试方法
按理说,第2代白盒测试工具已经很完善了,那第3代又是什么?
软件测试是一门复杂科学,支持自动测试与覆盖率评估后不见得就能成功实施白盒测试,尤其重要的是,第2代白盒测试解决了重复测试问题,但没解决持续测试问题。简单来说,重复测试使测试操作能以规范格式记录,当被测对象没变化(或变化很少)时,测试用例是可重用的,但如果源码大幅调整(甚至重构),或者按迭代模式不停追加新功能时,如何维持用例同步增长,并与源码一起同步更新,已经不是简单的增强用例复用能力就能解决的。因为代码更新与用例更新交织进行,测试用例与被测源码一样对等的成为日常工作对象,必然促使原有工作模式与测试方法产生变革,概括而言,白盒测试过程要从一次测试模式过渡到持续测试模式。
第3代白盒测试工具以xUnit为代表,包括JUnit、DUnit、CppUnit等,当然,我们列举xUnit工具,并不说这些第3代工具就比第2代工具要好。事实上,目前xUnit工具在功能上普遍赶不上第2代商用工具,许多xUnit工具甚至连基本的覆盖率都支持不了,况且,xUnit使用被测代码的编程语言写用例,普遍效率低下。这里,我们区别第2代方法与第3代方法,主要是测试理念上差别,而不以工具差别为基准,因为工具配套跟进还与诸多现实因素相关,是另一层面话题。
1.4第4代白盒测试方法的产生背景
xUnit是XP实践的重要支撑工具,XP作为一种软件开发方法论,总体虽然敏捷,但很脆弱,它对
第4代白盒测试尝试解决软件测试的深层次矛盾:测试的投入产出比问题。大家知道,研发资源总是有限的,你可以把测试人员与开发人员的比例配到1:1,也可以配到2:1,甚至5:1,但你做不到10:1、100:1,如果你有钱,也有人,完全可以按100:1或更高比例配置,这时所有测试瓶颈都没了,你可以让测试人员边喝咖啡边干活,因为每新写1行代码总有人编出100行脚本测试它,还怕产品不稳定吗?不过,疯子才会这么做,比尔盖兹有的是钱,一年捐款十多亿美金,但不见得微软旗下产品就经常让测试人员比开发人员多出一倍。我的意思是,测试资源必然是受限的,这个前提下我们才讨论第1代、第2代白盒测试向第3代、第4代演化的必要性。基于同样原理的xUnit工具,针对不同开发语言效果截然不同,这说明什么?说明这种实践的瓶颈仍在投入产出比上,也就是上面所说的1:1效果,还是2:1,抑或是5:1效果。
高效平台下的高效工具可以大幅提高测试效率,测试投入与开发投入之比小于1:1就能保证测试质量,项目就成功了,而低效平台下的低效工具,必然要投入更多测试资源(比方5:1)才能保证效果,拐点就在这儿,哪个公司禁得起5:1的测试投入?!从这个意上说,推出第4代白盒测试方法意义重大,我们要尝试解决决定项目成败的拐点问题。
事先申明一下,下文涉及持续集成与测试先行(或称测试驱动开发,TDD)实践,虽然这两者都是XP的重要组成部分,但我们无意宣扬XP,事实上,真正能适应XP的项目范围并不宽,跳过需求与预设计直接启动项目的做法,足以让客户敬而生畏,把文档丢给狮子,那是无政府主义散兵游勇行径。不过,XP确有许多闪闪发光的实践,持续集成只要运用恰当还是不错的模式,测试先行的理念也不赖,只要不过度实施就好。
2什么是第4代白盒测试方法
第4代白盒测试方法(4GWM)针对前几代测试方法不足提出,许多理念仍继承第2代与第3代测试方法。下表简要的列出第1代到第4代白盒方法的主要差别:
|
是否评估测试效果
|
是否自动测试
|
是否持续测试
|
是否调测一体
|
第1代白盒测试方法 |
否 |
否 |
否 |
否 |
第2代白盒测试方法 |
是 |
是 |
否 |
否 |
第3代白盒测试方法 |
是 |
是 |
是 |
否 |
第4代白盒测试方法 |
是 |
是 |
是 |
是 |
上表中,“是否评估测试效果”指是否有覆盖率或其它评估测试效果的指标,“是否自动测试”指是否形式化描述测试操作并将它用于再次测试,“是否持续测试”指是否以按持续集成的模式开展测试,“是否调测一体”指是否将测试设计高效的融入产品编码与调试的日常实践之中。
第2代白盒测试与第3代的分水岭在于“是否持续集成”,或许您会说,我的项目也是经常出版本,反复追加测试用例的呀,请注意,这是两个概念,Joel测试——改进代码的12个步骤中有一条:“编写新代码之前先修复故障吗?”,先修复故障是质量优先的项目,否则进度优先,这是两种完全不同的行事风格,前者时时测试,始终每写一两个函数就补全相关测试用例,测试实践是融入开发全过程的,而后者依时间表行事,测试仅是特定阶段里的任务。
对了,测试方法怎么跟软件开发方法扯上了?因为测试不是孤立的,测试是否有效强烈依赖于软件工程方法,就像早期的开发语言,只有assert语句与测试相关,发展到现有的C#,单元测试框架也是该语言的固有组件了。测试脚本也是一种产品代码,测试方法实际与软件开发方法密不可分的,这在第3代与第4代白盒测试中体现得很充分。
第4代白盒测试方法相对第3代方法,增加了将测试过程(包括测试设计、执行与改进)高效的融入开发全过程,这里,“高效的”是关键词,那如何才算高效呢?我们先简单了解4GWM在3个关键领域的9项关键特征,如下:
A. 第一关键域:在线测试
1、 在线测试驱动
2、 在线脚本桩
3、 在线测试用例设计、运行,及评估改进
B. 第二关键域:灰盒调测
4、 基于调用接口
5、 调试即测试
6、 集编码、调试、测试于一体
C. 第三关键域:持续测试
7、 测试设计先行
8、 持续保障信心
9、 重构测试设计
3为什么持续集成
为什么要持续集成?这个问题太重要了,我们专门拎出来讲,请大家先不急于跳过本章去看4GWM的9个关键特征怎么定义的。
3.1 JOEL测试
Joel是个怪人,当然他不认识我,我拜读他的Blog才知道他的。这家伙总有许多稀奇古怪的思想在小脑瓜里蹦达,他是“经常放猫出来闲逛”的人。科学研究表明,人的大脑只占体重2%,却消耗20%的能量,当大脑思考问题时,释放出的能量等同于夜间放一只猫出来活动。他的“Joel说软件”专栏(www.joelonsoftware.com)很火,有一些不乏真知灼见。比如,Joel测试——改进代码的12个步骤:
1、 有版本控制机制吗?
2、 能一步完成编译链接吗?
3、 每天都做编译吗?
4、 使用缺陷跟踪库吗?
5、 编写新代码之前先修复缺陷吗?
6、 有最新的进度表吗?
7、 有规格说明书吗?
8、 程序员拥有安静的工作环境吗?
9、 你用到了你资金能力内可买到的最好工具吗?
10、 有测试人员吗?
11、 要求新聘人员在面试时编写代码吗?
12、 进行走廊可用性测试吗?
每个问题可以回答“是”或者“否”,答“是”则加1分,得12分是完美,11分勉强接受,10分以下问题就大了,大家有兴趣看看你所在的组织能打多少分。
有测试人员吗?干嘛这么问,没测试人员还叫软件公司吗?这个问题并不可笑,还真有不少公司从未配置过专职测试人员。某白炽灯生产商在使用说明中特意声称,灯泡不能往嘴里塞,否则会出严重医学事故,说明书中还郑重其事的介绍灯泡不慎入口后,如何求医,如何抹润滑剂,如何左转90度右转90度慢慢取出来。有人觉得滑稽,谁白痴有事没事拿灯泡往嘴里送?即使放嘴里了也不用这么麻烦吧?非得试试,结果如何?怎么也拿不出来了,只得嘴里叼个灯泡打的上医院,最后,医生按照说明书费老劲才将那玩意卸下。所以,不要轻易否定前人经验,早有人试过了。
看看上面12个步骤,前5步活脱脱在讲如何实施持续集成,若进一步了解其内容,大家不妨浏览Joel的Blog原文。
3.2 持续集成不是XP专有实践
持续集成属于IID(持续迭代开发)方法学,在测试上,就现实而论是以xUnit实践为代表,持续集成概念被XP刻上深深烙印,但它确非XP专有实践。
早在20世纪60年代IBM的Federal Systems Division就开始应用IID开发模式了,源于IBM的集成产品开发流程(IPD)相对CMM,有个显著特征,它支持渐增迭代开发,虽然迭代频度比不上微软每日构造,但其理念仍是持续的迭代开发。有意思的是,IPD流程在华为公司本土化后,发展出“版本火车”理论,有点类似于Scrum实践了,版本火车不仅让产品(通常是大产品)版本发布更加规范有序(因为火车总是定点出发的),也推动研发以更快频度推陈出新。
但目前持续集成仍在有限范围能成功应用,微软无疑是个样板,毕竟纯软件产品容易实施每日构造,还有不少实践XP的项目,持续集成也运用得很成功。所以,就整体而言,持续集成能否成功,已经不是方法论问题,更多是IT工具如何支撑的问题。
3.3 为什么持续集成
我们看一个实际案例,某通信产品在V1版本编码完成时,进行过规范的单元测试活动,之后V2、V3要不断增加功能、修改功能,就放弃单元测试了,当V3最后市场交付时统计发现,相对V1版本,代码修改量已达到40%。QA从其中两个模块随机抽取100个问题单做缺陷分析,结果发现:第一个模块有50%的问题是在V1版本单元测试结束后引入的,而另一模块也有30%问题是单元测试后引入的。
也就是说,在第一次完整单元测试之后,代码修改了40%,也因此产生了40%的问题,由于增量白盒测试难以实施,这些问题都被遗留到后期功能测试中才发现。单元测试没能持续开展,带来后果是:发现问题不彻底,付出代价也更高。
上述模式在业界还普遍存在,我们称为一次测试,与持续测试不同,一次测试的测试设计只做一次,用例仍可重复拿来跑,因为测试脚本与源码不同步,用例维护是间歇进行的,或者干脆不维护。注意,一次测试与持续测试的差别不在于用例是否可重用,而在于测试设计的持续性。
许多企业做不到持续测试,其主要原因不是不想做,第一次测试都认真做了,追加代码或修改代码当然也要做测试,做不了是因为操作上存在困难。持续测试是需要一开始就规划,测试工具要配套跟进才能顺利实施的,对于老产品,代码修修补补,无论一次测试还是持续测试都很难做得好。
引入持续测试,不仅以更低代价发现更多问题,更重的是,它体现了一个组织在测试理念上有质的飞跃。一次测试是一种被动测试,开发人员受制于组织纪律(或主管、QA等压力)才去做,而持续测试是主动测试,大家在测试中尝到甜头,从原先不自觉状态,过渡到自发、自觉的时时做测试。这两种情形无疑有天壤之别,前面提到的Joel测试12步骤,实际上是微软实践,与持续集成相关的有5条,足见它的重要性,是否引入持续集成,以及实施的效果如何,实际反映了一流公司与二流公司的差距。
4第4代白盒测试方法的关键特征
白盒测试是一项实践性很强的技术,我们讲第4代白盒测试方法,离不开相关测试实践,尤其是测试工具支撑。本文的上篇先从理论上介绍什么是4GWM,下篇则结合具体测试工具介绍4GWM的典型实践。
4.1在线测试
4GWM第一个关键域是在线测试,包括3个关键特征:
² 在线测试驱动
² 在线脚本桩
² 在线测试用例设计、运行,及评估改进
一次白盒测试中(即一个用例中)我们关注被测单元功能是否实现,被测单元作为整体,在特定环境下运行(比如某些全局变量取特定值、某些依赖线程或任务已启动等),具有特定的输入输出,这几项都属于“测试驱动”。另外,被测单元若能正确运行,还依赖它调用的子函数是否提供正常功能,这些子函数我们称为“测试桩”。分层结构如下图:
在三层实体中,被测单元是测试关注对象,要求尽可能真实,我们设法维持其原状,测试驱动与测试桩可以模拟(或叫仿真),允许存在一定失真,但要求尽可能高效,否则测试产出的拐点问题解决不了。
4.1.1脚本驱动与脚本桩
先回答一个基础问题,编写测试用例应优先采用脚本语言,而不与被测代码使用同一的语言,为什么?
还是应为软件测试的深层次问题——投入产出比,如果被测编程语言的抽象度较低、封装性差,用起来就很麻烦。比如拿C或C++写测试用例,得处处小心内存操作,要正常申请释放、注意不越界,时常关心使用变量是否安全、是否已初始化等。也许有人说,不对, CppUnit中拿C++测C++,我用得很爽呀?噢,没错,我得先恭喜这位老兄,安于现状不失为一种好品质。
我们设想一下,编写一万行C++代码,你要写多行代码测试它,一千行?两千行?不对,是一万行,按业界普遍规律,测试代码行至少要与被测代码行数相当才见效果,测试代码要不要调试?当然要调,天哪,算出来的了,测试投入至少是开发投入的三、四倍才做得下来(后期还有功能测试、性能测试、兼容性测试等等,还要占用大量精力),这样的项目是不是处在能否成功的拐点上?所以,如果您还在用C、C++等过程语言写用例,请尽快换到脚本语言,如python、ruby、CSE等,用脚本语言能让你编写用例的效率提高3到5倍。
用脚本编写用例,意味着测试驱动与测试桩仿真也用脚本语言。我们看一下VcTester工具使用的测试脚本,假定被测对象是C代码的冒泡排序算法:
@H_404_2299@@H_306_2301@void BubbleSort(OBJ_DATA_PTR *ObjList,int iMax)
{
int i,j,exchanged;
OBJ_DATA *tmp;
for (i = 0; i < iMax; i++) // maximum loop iMax times
{
exchanged = 0;
for (j = iMax-1; j >= i; j--)
{
if ( ObjCompare(ObjList[j+1],ObjList[j]) < 0 )
{ // exchange the record
tmp = ObjList[j+1];
ObjList[j+1] = ObjList[j];
ObjList[j] = tmp;
exchanged = 1;
}
}
if ( !exchanged ) return;
}
}