回归测试最佳实践
 

2009-03-31 作者:邓 俊宁,黄 胜,陈 旸 来源:IBM

 
本文内容包括:
本文介绍一个有效的解决方案,可以提高回归测试的效率与质量。它解决了回归测试中的两个主要问题:如何优化回归测试用例以及分析覆盖率。

回归测试对保证软件质量具有重要意义。要实现有效的回归测试,必须解决回归测试中的两个主要问题:(1)测试用例的优化选择;(2)覆盖率分析。前者决定了回归测试的效率,好的测试用例的选择可以用少量的测试用例准确地覆盖新版本中尽可能多的改动。后者是度量测试的重要指标,通过达到良好的测试覆盖率,保证了回归测试的质量。

本文正是通过讨论如何优化选择测试用例,用最小的代价达到最大的覆盖率,从而找到回归测试的有效解决方案。

存在的问题

测试用例的优化选择

对测试用例的选择,测试人员通常有两种作法。一种是,把相关的或是所有的模块的测试用例都选出来执行一遍;另一种是,仅针对被 Fixed 的 APAR/Defect 进行检验,测试用例很少或是开发新的针对这个 Fixed 的测试用例。这两种方法都存在不足。第一种方法在测试时间有限的情况下,去执行所有的测试用例,会测试到很多无需再测试的测试用例,从而导致测试资源的浪费;第二种是很难确保 APAR/Defect 改动后,被测系统没有受到关联影响,很难保证测试质量。

由于 Bug Fix 或者功能更新后,在新版本发布之前,我们要确保所作改动不会对已有的功能模块产生负面的影响。用所有的测试用例作回归测试, 存在着人力与时间成本过高的问题;依靠人的经验去挑选回归测试用例,存在着挑选不准确或对程序改动测试覆盖不全的问题。

图 1. 测试用例优化选择问题
图 1. 测试用例优化选择问题

回归测试覆盖率分析

测试用例优化选择可以有效地解决现有测试用例的覆盖问题,但在实际测试过程中,我们仍然发现存在着覆盖不全的问题。即新版本中的某些实现并不能被现有的测试用例所覆盖。这样,测试人员需要开发新的测试用例对新的功能进行测试覆盖。然后对于测试人员来说这并不实际,他们很难依靠测试经验将代码中没有被测试的地方找出来。那么如何更好的得到测试过程中的测试覆盖率,及测试结束后整个软件的测试覆盖率呢,从而使得测试人员可以针对未被测试到的地方设计新的测试用例。这里我们特别针对在版本更新过程中,那些发生了更新却没有被覆盖到的地方。

解决问题

原理

图 2. 测试用例优化选择原理
图 2. 测试用例优化选择原理

图 3. 测试用例优化选择举例
图 3. 测试用例优化选择举例

如上图所示,所有的测试用例都会有一个函数调用的路径。我们把这些调用路径一一记下来。对于新版本所作的改动,所有与之相关的上层调用的测试用例都能够准确地选出来,这样我们就能用这些准确的测试用例来覆盖这次改动所产生的影响。毫不相关的测试用例则不会被选出来。从而用较小的成本完成这次改动所需要的回归测试,既省时省力又保证较高的测试质量。

图 4. 覆盖率分析举例
图 4. 覆盖率分析举例

如上图所示,在版本更新过程中受到影响的测试用例为 Test Case1, Test Case2, Test Case3 覆盖了 12 个 Node,在版本更新过程中的更新点是 4,其中被覆盖到的为 3,还有 1 个更新点没有被覆盖到,现有测试用例集合的更新覆盖能力为 75% 。这样,我们知道上一个版本的测试用例设计不够充分尚有程序模块没有被任何已有的测试用例所覆盖。由于代码的更新最有可能引起程序的缺陷甚至系统崩溃,所以需要添加新的测试用例以保证对更新的覆盖,以降低程序运行的风险。

对于软件的节点覆盖率,我们细化到函数粒度。我们是通过软件部署的 Binary 代码分析得知的函数情况。不需要借助源代码,因此对于软件进行测试覆盖率分析来说要求低,用较小的成本完成此次的测试用例覆盖反馈与分析。既省时省力又保证较高的测试质量。

解决方案

基于对问题 1) 和问题 2) 的原理分析,我们设计并实施了回归测试的解决方案,如下图所示。它包含了 3 个主要步骤。一是测试用例的录入;二是对新旧两个版本的变更分析;三、通过测试用例优化选择和覆盖率分析,得到相应的测试用例优化选择报告,和覆盖率分析报告。

图 5. 回归测试解决方案
图 5. 回归测试解决方案

步骤一, Trace Test Case 负责录制测试用例,并将捕获到的测试用例的 Runtime Trace 存放到数据库中;

测试用例在后台运行中的 Runtime Trace 是动态分析 (Dynamic Analysis) 中的重要信息。这些实际的运行信息为测试用例的优化选择和覆盖率分析创造了条件。下面是测试用例跟踪的框架图:

图 6. 测试用例跟踪的框架图
图 6. 测试用例跟踪的框架图

从上图我们可以看出,测试人员触发 Trigger 之后,会启动 Agent Controller 。 Agent Controller 一直对 JVM 中的 JVMTI 进行监听,以获取部署在 JVM 上的被测应用程序。这些 Agent Controller 还负责将收集到的数据传输给 Data Collector 。又 Data Collector 将这些 Runtime Trace 写入如下表所示的数据库表中。

Case ID Package Class Method Signature
001 com.ibm.crl.orts.action DeleteCommodityAction Delete ([Ljava/lang/String;)V
001 com.ibm.crl.orts.action DeleteOrderAction Delete  
002 ......      
003 ...... ...... ...... ......

注意:函数的 Signature 信息作为函数的参数标识也需要记录下来。以区别同名不同参数的函数。

步骤二, Change Analysis 用于将新旧两个版本作比较,得到 Change Report,即程序变更报告,可以精确到 Method 粒度。一般来说代码变更有 4 种级别,分别为包级别 (Package),类级别 (Class),函数级别 (Method) 及语句级别 (Statement) 。

对于包级别和类级别来说,比较的力度过粗,会影响到回归测试优化的质量。而函数级别和语句级别都能起到很好的回归测试的作用。其中语句级别因为粒度最细,等到的分析结果也最精确,回归测试质量最高。但与函数级别的变更分析相比,回归测试的质量相差很有限,但造成了过多的执行时间代价,影响了回归分析的效率。因此我们采用函数级别的变更分析作为回归测试的变更粒度。

确定比较粒度之后,可以选择分析比较的方法。最简单的常用比较方法就是文本比较。包括源代码和可执行文件 (binary code) 的文本比较。根据 Java 语言面向对象的特点,还可以采用基于面向对象分析的比较方法。后者得到的分析粒度更细,但是所花的代价也越高。

步骤三, 在通过测试用例录制得到测试用例具体的 Runtime Trace 信息,以及通过 Change 分析得到新旧两个版本的变更信息之后,我们可以对测试用例优化问题及覆盖率分析问题进行求解。

Test Case Prioritization 中,通过测试用例与运行的 Runtime Trace 进行匹配得到相关的测试用例。并利用测试用例优化排序算法对相关的测试用例进行排序,得到优化结果。现在的优化排序算法有多种,这里不对优化排序算法进行讨论。

Coverage Analysis 中,通过对已被相关测试用例覆盖的 Method 数量,及未被测试用例覆盖到的 Method 数量的分析,可以得到基于代码更新的覆盖率报告。测试人员得到这份报告之后,可以找到未被覆盖到的 Method,并对其进行针对性的开发新的测试用例。以完成对新功能的覆盖。

我们可以看到步骤一,二共同服务于测试用例优化选择和覆盖率分析两个模块。有了测试用例的 Runtime Trace 和 Change Report. 我们可以将 Change 结果与 Runtime Trace 进行匹配,找出能覆盖程序更改的测试用例。同样,对于没有被测试用例覆盖到的 Change 部分。由于没有相关测试用例被找出,我们现有的测试用例是不足的,需要针对未被覆盖到的 Change 部分开发新的测试用例。

而覆盖率作为软件测试的一项重要指标,可以根据已经得到覆盖和未被覆盖的 method 进行求解,即已得到覆盖的 change method 数与总的 change method 之比。

结果

基于以上的回归测试解决方案,我们对一个有着 30 个测试用例的程序进行回归测试,得到如下测试用例优化选择分析报表:

Change Analysis Report

总函数 变更函数 覆盖数 未覆盖 覆盖率
108 12 10 2 83.3%

表 1 优化选择测试用例: 3 (of Total 30)

Case Name Cover Changed Method
TestCase001 5 details
TestCase005 3 details
TestCase013 2 details

分析报告显示这次代码变更共有 12 个函数发生了更改。在 30 个测试用例中有 3 个测试用例与这些更改相关,它们覆盖了这次代码更改 12 个中的 10 个。而其它 27 的测试用例则与这 12 个代码改动毫不相关。

表 2 回归测试结果分析
 
全部测试用例 优化选择 函数变更 覆盖率分析
已覆盖 未覆盖 覆盖率
30 3 12 10 2 83.3%

从表中我们可以看到,经过测试用例优化选择之后,我们选出了 3 个和函数变更相关的测试用例,达到了 83.3% 的覆盖率。同时由于 27 个与函数变更无关的测试用例不需要重测,使得 90% 的回归测试资源得到了节省。

图 7. 覆盖率分析
图 7. 覆盖率分析

从上图,我们可以清楚地看到基于每个函数改动的相关测试用例的数目,以及测试覆盖率。比如 ManageCommodityAction 这个 Class 里面,存在了 2 个 Change Method, 只有 1 个 changed method 被现有的 1 个 Test Case 所覆盖,测试覆盖率为 50% 。

上面分析报告中总共有 12 个函数发生改动,基中还有 2 个没有被任何测试用例覆盖到。那么未被覆盖的 Change Method 就需要测试人员对之进行分析并且添加新的测试用例以提高我们的测试覆盖率 , 保证测试质量。

总结

回归测试用例的优化选择,以最少的测试用例,准确地覆盖所作改动,极大地提高了我们回归测试的测试效率与测试质量。

自动测试过程中的覆盖率反馈分析,以最小的测试代价,最精确的分析,来获得当前的测试完成情况,为测试人员提高了良好的分析报告,以便测试人员改进和新增新的测试用例。大大提高了回归测试的测试效率与质量。

参考资料

学习 讨论

火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织