XXXX作为一个新项目,和其他所有项目一样,在开发工作进行之初就在考虑如何保证代码开发的质量。答案很容易找到:充分的单元测试。但是以前真正做得好得项目却不多。
经过分析,总结了一下做好单元测试工作的四个要素:
――思想上的重视
――计划上保证
――测试手段保证
――测试效果的可验证
1.1 思想上重视
从以往的开发过程总结了一些教训:
――开发人员模块在交付联调前,测试不充分,导致联调周期较长
――代码进入维护期后,修改代码往往引起不可预期的错误。导致开发人员比较害怕在相对稳定的代码上进行修改。
由于有这些教训,所以在编码之前,项目从领导到员工都很重视单元测试工作。
1.2 计划上保证
计划制定经过了XXXX的分解流程,XXXX分解小组成员讨论时充分意识到了单元测试工作的必要性,也意识到了为之要付出的代价。最终确定了单元测试和代码开发的比重为2:1左右。以操作维护的配置模块为例,最终制定的计划如下:
从计划中可以看出,花在单元测试上的时间和代码开发的时间的比重甚至超过了2:1。
附:表方法测试代码和被测代码的行数统计数据(该部分测试覆盖率已达95%以上)
从数据看来,我们当初的估计是对的。测试代码行数和被测代码行数的比值大致是2:1
1.3 测试手段保证
1.3.1 测试框架
对于一个系统工程来说,一个好的框架是至关重要的。一个好的框架应该有如下一些特点:
――结构清晰。不同的粒度层次在框架中一目了然
――可扩展性良好。
我们选择单元测试工具就是为了满足能基于该工具搭建一个好的单元测试框架,另外还有以下几个方面的考虑:
――整个开发组负责三个模块:数据库,操作维护,传输。每个模块由不同的开发人员负责。从测试代码的可维护性考虑,需要统一各人编写测试代码的框架以及风格
――前台代码是由C语言编写的
――单元测试应该是可回归的,自动化的
最终我们选择了Cunit,因为Cunit完全符合我们的要求。
–提供很多实用的接口函数(比如:各种断言、信息汇总、打印等)
–简单、安全、方便
–可添加任意多个测试单元
–逐步添加,汇总成套,全面回归测试
–完全免费
测试框架选定以后,在组内进行了相应的培训,使得每个人的认识达到相同的水平,尽可能保证以后测试风格的一致。
1.3.2 测试方案和测试规程
单元测试是一个系统的工作,在进行之前需要一个总体的规划。这个工作在单元测试方案中得到体现;测试规程是对测试方案的细化,是单元测试的指导。测试规程可以在notes的测试管理平台中录入。
1.3.3 自动化测试的考虑
一个单元测试是由许多测试用例构成的,要实现可回归,自动化的测试,每个测试用例都应该可以按照一定的顺序连续的执行。要达到这个目标,就要求对整个单元测试做一个很好的规划。以数据库模块为例,我们做了如下的规划:
● 数据环境的建立
数据库的测试依赖于一套数据表。单元测试一开始就需要在表中插入数据,以建立后续测试用例依赖的数据环境
● 测试粒度合理的划分
我们把被测对象分为表方法、查询接口和修改接口。
注:测试用例套(TestSuite)是Cunit的一个概念,用于封装测试用例套或者测试用例(TestCase)
● 在某种粒度上面保证数据环境的松耦合
所有的测试用例基于数据环境编写,数据环境对于数据库的单元测试是及其重要的。每个测试用例从前一个用例继承数据环 境,又将本测试用例修改后的数据环境移交给下一个测试用例。从这个过程可以看出,数据环境在测试用例间是紧耦合的。如果测试用例一多,数据环境就会变得难 以控制,用例的编写将变得非常麻烦。尤其是多人分工合作的情况下,简直变成了不可能的任务。
幸好我们有测试套!
以表方法的测试套为例,在第二级粒度上,一个套就是一张表所有表方法测试用例的集合。这个套包含的测试用例大致在 4、5个左右。我们的策略是:二级粒度测试套内部的测试用例紧耦合数据环境,而二级粒度套之间的数据环境为松耦合。这通过在每个二级粒度套内加入一个数据 环境恢复用例来实现。
这样一来,我们可以按照二级粒度的测试套来分工编写测试代码。
单元测试是不依赖其他模块的测试。但是实际代码中存在对其他模块函数的调用。为了消除这种依赖关系,就需要编写桩函数来替代对外部模块函数的调用。
为了清晰起见,我们的桩函数集中在一个文件中。
桩函数只是一个实现,至于原形定义,还是要引用其他模块的头文件,这样可以跟踪其他模块函数接口最新的改动。
● 单元测试工程的规划
一个清晰合理的单元测试工程规划是必要的,这样有利于对象的查找和维护。
以DBS模块的单元测试为例,我们的工程结构如下图所示。
1.3.4 测试效果的可验证
如何来量化单元测试的效果?
覆盖率是一个最直观的指标。我们采用了Rational公司的Pure Coverage工具来获取覆盖率。这也是公司推荐的一个简单易上手的工具。我们要求测试覆盖率达到90%以上。
2、自动测试
2.1 每日构造
每日构造是公司推行的一个最佳实践。它有一下一些功能:
● 开发人员及时把最新代码放入代码库,及时check out和check in,避免积累大量代码
● 及时进行模块间的整合,及时发现问题
● 对部分功能进行测试,无需等待
● 使用测试用例工具,对功能进行完整和重复的检验
● 记录所有程序问题
● 实现解决Bug的自动流程
● 提高正式版本提交的质量和速度
● XXXX在软件上开始推行每日构造。
2.2 冒烟测试
如果在每日构造的同时进行“冒烟测试”,能最早发现版本的问题。“冒烟测试”是微软提出的名词。其
对象是每一个新编译的需要正式测试的软件版本,目的是确认软件基本功能正常,可以进行后续的正式测试工作。“冒烟测试“的执行者是版本编译人员。
ZXWR-RNS网管项目已有用Robot在网管产品进行冒烟测试的实践。但是对于前台软件,公司内部尚未有这方面的尝试。
2.3 自动测试
基于前期模块的单元测试工作,一个自然的想法就是在每日构造的框架上运行模块的单元测试代码,实现前台软件的每日自动化测试。这是比“冒烟测试”更全面,更彻底的一个测试。
2.3.1 单板软件
模块1
模块2
模块n
模拟测试工具
XXXX软件的架构
2.3.2 自动测试的规划
2.3.2.1 自动测试的级别
从软件的构成来考虑,分三级构造测试工程:
● 模块单元级:基本的软件单元
● 板级:由多模块组成的单板软件
● 系统集成级:由模拟测试软件和单板软件组成
这三个级别的测试是互相补充的关系。每日构造进行三个级别的测试,这样就比较完整了。
2.3.2.2 测试代码的管理
要实现每日构造并进行自动测试,必须将测试代码也提升到被测代码同等的地位,纳入CC的管理。在CC上的目录结构如下所示:
BoardSWS
-BoardTest \\板级测试工程目录,包含测试用例代码以及板级测试工程
-SystemTest \\系统集成级工程目录,不含测试用例,仅含工程
-SCMM
-UnitTest \\模块单元级测试目录,包含该级测试用例以及测试工程
-SCMM源码
-Cunit \\模块单元级测试用Cunit文件
-其他模块
-Cunit \\板级测试用Cunit文件
SimuRNC
-SystemTest \\系统集成级测试工程目录,包含测试工程和测试用例
-SimuRNC源码
-Cunit
基于这样的目录结构,发布代码和测试代码在CC中纳入同一个VOB管理比较方便。这样一来,测试代码和发布代码执行相同的CC-CQ关联策略。也就是说,修改测试代码也需要和CQ关联。
2.3.3 自动测试的执行
自动测试由版本室完成。自动测试执行分为四个步骤:
● 同步CC的文件到本地
● 编译各测试工程(VC环境),输出编译结果文件
● 执行编译结果,输出测试执行结果文件
● 将编译结果导入
notes
上述步骤都是通过编写一个BAT文件挂在WINDOWS的“任务计划”中每日自动完成。
附:
BAT文件(DBS模块)
编译结果 (DBS模块)
测试执行结果(DBS模块)
2.3.4 自动测试的目标
依附于每日构造机制,自动依序运行三个级别的自动测试,测试产生的结果输出到文本文件。然后通过自动分析脚本提取该文件中没有通过的用例信息,发送邮件通知相关责任人进行分析处理。如此做到“日事日毕”,将故障扼杀在萌芽状态,保证代码质量的稳定。
本文出自seven的测试人生公众号最新内容请见作者的GitHub页:http://qaseven.github.io/