本文从代码覆盖率的基本概念开始,详细介绍测试工程师如何使用 Contest 来分析测试用例的代码覆盖率,接着结合 FoCuS 来产生测试用例的源代码覆盖率报表,从一个特殊的角度来度量测试用例的优劣。同时通过一个实际例子来讲解取得代码覆盖率的基本流程,其间贯穿一些基本概念,并且在得到代码覆盖率的基础之上分析如何改进测试用例,在反复迭代的过程中来提高测试用例的代码覆盖率。您可以在短时间内了解代码覆盖率的概念,并且应用作者介绍的流程来发现测试用例的漏洞,以及分析改进测试用例。
解决什么问题
问题的提出时这样的:对于测试人员来说,首先面临的问题就是无法度量测试用例的质量,如果测试工程师花费大量时间写的测试用例不能有效地覆盖重要的实现代码,那么可以表明这样的测试用例不是优良的。同时可以根据测试覆盖了的报表来分析为什么没有覆盖到重要的代码,接着需要进行改进测试用例的代码覆盖率达到满意的结果。代码覆盖率高低根据产品的不同而不同:70%,80% 甚至 100% 都是可能的。对于测试工程师来讲,可以遵循这样的流程 : 获取覆盖率 – > 发现未覆盖的代码 – > 添加新测试用例来改良测试用例,保证产品质量。
代码覆盖率基本介绍
在 CMMI4 体系的测试过程中定义了四个度量指标:代码覆盖率、测试执行率、测试执行通过率、测试缺陷解决率。其中代码覆盖率就是描述了程序的源代码在功能测试中的覆盖率,即反映测试用例对被测软件的实现代码覆盖程度的重要指标,它也是对测试工作进行量化的重要指标之一。测试工作往往不如开发那样可以用图表数据表示结果,一个重要原因之一就是测试难于量化,而代码覆盖率恰恰是解决这一问题的重要指标。同时一个角度来讲,软件的风险很可能就存在于代码覆盖率没有涉及的地方,代码覆盖率可以发现其中未覆盖的场景(Test Hole), 测试工程师根据这个 Test Hole 来分析测试用例,以此来优化测试用例,这个过程能显著提高测试的生产效率,并提高软件产品质量。代码覆盖率是个白盒测试的概念,毕竟最后要跟踪到源代码。一般来说,代码覆盖率的测试应该是在产品的中后期引入,不过对于使用敏捷技术开发的产品,可以在开始开发的迭代周期就引入。笔者所在的团队就在项目的中后期使用,主要用于测试回归测试的代码覆盖率。
代码覆盖率基本的指标
语句覆盖 Statement coverage (line coverage): 用于评测产品代码独立代码行在测试活动中执行的行覆盖率,优点是简单,缺点是无法包含代码的逻辑分支。
基本语句块覆盖 Basic block coverage: 把非分支的代码区域做为一个计量单位,用于一个 if/else 中的某个分支行数远大于另一个分支,所以可能会出现这项指标比较高的情况。
条件覆盖 Decision coverage (branch coverage): 用于评价代码分支地代码覆盖率。
功能覆盖 Function coverage (Method Function): 评测产品代码在测试活动中执行方法的覆盖率。
代码覆盖率的实现方式
对于 Java 作为开发语言的产品,分析代码覆盖率时候使用 Instrumentation 技术,本质就是在产品代码的关键位置插入统计代码,即在代码的每个方法的开始加入统计计数器和序列号,当方法被执行之后,根据序列号以计算改方法被测试用例测试到。一般来说,Instrumentation 技术可以分为两种方式:Class Instrumentation 和 Source Instrumentation。前者把统计代码插入编译好的 .class 文件,而后者则把统计代码插入源代码 (.java 文件 ) 并编译成新的 .class 文件。大多数代码覆盖率工具采用这两种 Instrumentation 技术。其中用 Class Instrumentation 使用最广,因为在产品开发的每个阶段,采用持续集成的方式来开发产品,测试工程师面对的不是产品的源代码,而是交付给测试的产品版本,所以考虑到避免修改源代码,而直接注入统计代码到生成的 class, 对于本文中也用第一种 Class Instrumentation 技术。
Contest 介绍
Contest 是功能强大的易用的工具,在测试的前期用以发现并行系统的 bug, 而不用建立复杂的测试多处理器和多应用的测试环境。Contest 对开发人员可测试工程师都很非常有帮助,它可以作为独立运行的工具或者一个 Eclipse 插件,开发人员去运行可以得到单元测试的代码覆盖率。Contest 可以用最小的成本来度量代码覆盖率,在调试期间,可以发现死锁信息,Contest 还有很多其他功能,代码覆盖率是我们在本文中用到的一个功能。有兴趣的读者可以在这里下载 Contest。Contest 支持两种级别的代码覆盖率,功能覆盖 method coverage 和基本语句块 basic-block coverage。
分析代码覆盖率简单的四个步骤
第一步:Instrumentation 您的应用
Instrumentation 部署的应用就是将代码覆盖率的统计代码插入到您的应用中去的过程,支持 J2EE 的应用有:web application, 包括 war 部署包和 ear 部署包。支持 J2SE 的 C/S 系统中的 Java 应用。在做 Instrumentation 操作前必须先 Contest 运行环境,让类路径能找到 contest.jar 文件即可。
伴随 Instrumentation 操作有一个 preference 文件,其记录需要运行 Instrumentation 的参数,名字为 King.Properties。
这里主要关注四个参数即可,其中 method Coverage 和 basic-Block Coverage 是二选一的,不能同时为 true。
methodCoverage = true // 选择功能覆盖率
basicBlockCoverage = false// 选择 basic block 覆盖率
buffer_size = 2048 // 运行 instrument 的缓冲大小
output = . //Instrument 的输出目录的路径
instrument 命令
对于部署包是 war 的脚本 :
java -classpath yourFolder:Contest.jar; com.ibm.Contest.instrumentation.Instrument yourFolderYourApp.war
对于 C/S 的 jar 脚本 :
java -classpath yourFolder:Contest.jar; com.ibm.contest.instrumentation.Instrument your1.jar your2.jar your2.jar
执行编写的脚本,就运行 Instrument 命令,在输出目录的路径上得到 com_ibm_contest,这是个 instrument 的输出目录。然后就会得到另外一个结果文件,比如您原来的部署包是 YourApp.war, 然后就会有新的 YourApp.war 文件,Size 比原来的大一些,原来的就会备份为 YourApp.war_backup。
第二步:部署您的部署包到服务器上
在前一步我们得到一个 YourApp.war 部署包,然后登陆您的服务器,比如是 Jboss 服务器,找到 Jboss/server/default/deploy, 用您改造的 YourApp.war 替换原始的 YourApp.war, 然后启动应用服务器,让改造后 YourApp.war 开始运行。经过以上两步,在运行服务器上代码覆盖率的环境就建立成功了。
第三步:运行测试用例
在这一步测试人员可以手工执行测试用例,也可以运行自动化测试,笔者就是运行自动化测试来执行测试用例。
第四步:得到代码覆盖率
在前三步运行完成后,就会产生 com_ibm_contest 目录,以 Jboss 为例,这个目录在目录下被生成在 Jbossin,记录着代码覆盖率的跟踪文件 trace file。目录结构如图 1 所示。
图 1. Contest Instrument 生成的目录结构
FoCuS 是一款生成各种报表的工具,与 Contest 结合,根据 Contest 的 Task 文件和 Trace 文件可以生成各种需要的报表,如代码覆盖率报表、测试漏洞报表等等。
通过 FoCuS 得到代码覆盖率的过程如下:启动 FoCuS, 运行 YourFolderJavaConTestFoCuSFoCuS.bat,如图 2 所示。FoCuS 的使用可以参考帮助文件,在这里,首先必须导入 Task 文件,如图 3 所示。
图 2. FoCuS 启动界面
接下来选择在第一步产生的 Task 文件目录,如图 3 所示:
图 3. 选择 Task 文件
导入 Task 文件后,FoCuS 需要创建一个模型文件,模型文件相当于工程文件,包含所有的 Task 文件和 Trace 文件,我们命名为 Coverage, 如图 4 所示。
图 4. 创建新的模型文件
接下来弹出一个消息窗口显示全部的 Task 文件数目,这个数目和您在第一步生成的 Task 文件相同,如图 5 所示。
图 5. 所使用的 Task 文件的提示界面
接下来选择所有的 Task 的文件属性,如图 6 所示。
图 6. 选择 Task 文件的属性
接下来要选择生成报表,选择 Code-Coverage->Hierarchy (Drill Down). 如图 7 所示。
图 7. 生成报表菜单
在这一步请选择 Trace 文件,然后运行所有的测试用例后生成 Trace,如图 8 所示:
点击 Add 按钮,然后选择 Trace 文件所在的位置。
图 8. 选择 Trace 文件界面
选择完 Trace 文件后,点击 Create Report 按钮,这样就生成代码覆盖率的报表,生成报表的时间根据 Task 文件的数目而不同,最后生成的报表如图 9 所示。
图 9. 生成的报表
图 9 第一行显示代码覆盖率为 49.2%,显示总共有 5593 个方法需要被测试,其中您的测试用例覆盖到 2752 个方法,两者的相除的结果就是代码覆盖率。接着再看红色部分显示的是没有覆盖的代码(覆盖率为 0%),绿色部分显示全部覆盖的代码 ( 覆盖率为 100%),黄色部分显示的是只有部分代码被覆盖。
同时我们可以创建测试漏洞 (Test Hole) 报表,选择 Functional-Coverage -> Holes (Drill Down),如图 10 所示。
图 10. 选择测试漏洞菜单
在这个窗口中,在目标路径上选择 Trace 文件,然后点击 Create Report 按钮,这样就生成测试漏洞报表,这个报表显示所有的没有被测试用例覆盖的代码,报表图 11 所示。
图 11. 生成测试漏洞报表
如果我们需要看看那些代码没有被覆盖,直接点击 Substring 的项目去看详细内容,比如我们选择 delete 项目,点击进去看到如图 12 所示。
图 12. 测试漏洞详细报表
在这个图表中,比如我们看到在目录 compaswdmsuiserviceserviceImplquicklaunch 中的 UserPreferenceServiceImpl.java 源文件,检查到 deleteApplicationGroup (com.pasw.dms.userprefs.TypeApplicationBookmark) 方法没有被覆盖,这个报表非常明确地告诉测试人这个方法没有被测试到,那么我们就检查测试用例,是否有对应这个方法的测试用例,接着可以补充这个测试用例以提高代码覆盖率。以此类推检查所有没有被覆盖到的方法,并分析和补充缺失的测试用例。
总结
我们可以免费地获得 Contest 和 FoCuS 工具,并用于项目开发。这两个工具使用方法很简单,这样减少学习工具的时间开销,使测试人员只需要把时间花销在撰写测试用例层面。Contest 是一个用于检测和报告 Java 代码覆盖率的开源工具。它不但能很好的用于小型项目,很方便得得出覆盖率报告,而且适用于大型企业级别的项目。Contest 支持两种方式的代码覆盖率分析,用户可以根据自己的需要进行选择。
FoCuS 工具可以很方便地针对本机以及远程机器上的 Java 程序进行代码覆盖分析,分析报告可自动生成。FoCuS 可以精确地找出被覆盖以及没有被覆盖的方法,以多种报表来展示给用户,通过覆盖率数据,可以知道测试得是否充分,测试得弱点在哪些方面,进而可以指导我们设计能够增加覆盖率的测试用例。
代码覆盖率在实际应用中常与自动测试相结合以达到最好的效果。自动测试过程中的代码覆盖率分析,以最小的测试代价,最精确的分析,来获得当前的测试完成情况,为测试人员提高了良好的分析报告,以便测试人员改进和新增新的测试用例,大大提高了回归测试的测试效率与质量。