使用 EMMA 获得功能测试覆盖率

测试覆盖率是评价测试完整性的重要的度量标准之一。 EMMA 是一个面向 Java 代码的测试覆盖率收集工具。在测试过程中,使用 EMMA 能使收集和报告测试覆盖率的过程更加灵活、简单。在本文中,作者将 EMMA 引入到功能测试的过程,详细介绍 EMMA 在功能测试中的使用方法、步骤,并对覆盖率结果进行分析。

1 评论

伞 云飞 (sanyunf@cn.ibm.com), 软件工程师, Author11 company

2008 年 6 月 26 日

  • 内容

在 IBM Bluemix 云平台上开发并部署您的下一个应用。

开始您的试用

引言

EMMA 是一个开源、面向 Java 程序测试覆盖率收集和报告工具。它通过对编译后的 Java 字节码文件进行插装,在测试执行过程中收集覆盖率信息,并通过支持多种报表格式对覆盖率结果进行展示。 EMMA 所使用的字节码插装不仅保证 EMMA 不会给源代码带来“脏代码”,还确保 EMMA 摆脱了源代码的束缚,这一特点使 EMMA 应用于功能测试成为了可能。

注意

  • 在测试中使用 EMMA 收集覆盖率信息之前,需要从 EMMA 的网站上下载 emma.jar 包。在这个网站上还可以得到更多关于 EMMA 的资源。
  • EMMA 只能收集 Java 代码的覆盖率。

文章附录提供一个样例代码,包含一个 WAR 包和一个 JAR 包,其中需要将 WAR 包安装在WebSphere Portal Server 上运行。在实际测试过程中,可以将它们替换成对应的被测对象。

 

回页首

功能测试中使用 EMMA 的优点

EMMA 收集的数据包括类覆盖率、方法覆盖率、块覆盖率和行覆盖率,这些数据以包为单位进行组织。

大多数功能测试中,测试人员一般不能直接得到被测源代码,源代码也不是测试人员关心的重点。在具体的测试过程中,功能测试人员一般以一个有意义的功能模块作为测试关心的重点,而能够反映一定功能含义的类和方法的覆盖率在功能测试中更有价值。因此,在功能测试中,类覆盖率和方法覆盖率是测试人员关心的重点,行和块覆盖率则作为测试的参考。

测试覆盖率报告中包含了两个方面的内容,测试覆盖的部分和未被测试覆盖的部分。尽管百分之百的测试覆盖率不能代表被测对象完全没有问题,但是测试覆盖的部分以及覆盖比率可以增加测试者对测试工作的信心,指导测试执行以及测试的方向。另一方面,当测试用例执行出现异常时,针对每个测试用例的测试报告还可以提供可疑代码的范围,为代码纠错提供帮助。

测试覆盖率报告中未覆盖的部分也同样有价值:

  • 表明测试可能不完整,有些功能、代码没有被测试覆盖到。
  • 为测试用例的设计提供指导建议。在覆盖率报告的指导下,测试人员有目的地与开发人员进行讨论,确定未覆盖部分是测试的空白还是不需要测试的部分。
  • 帮助开发人员发现无用代码,为修改,完善代码提供依据。

在使用 EMMA 获得测试覆盖率过程中,类、方法等覆盖的百分比报告,可以方便测试人员更好的评估测试。测试人员通过对照覆盖率报告与测试用例设计文档,需求文档可以迅速找到测试的不足。通过与开发人员进行讨论,可以更好的评估测试力度,并指导进一步的测试。因此在功能测试中引入覆盖率信息,能够完善测试结果报告,确保测试质量和力度,保证测试按质、按量地完成。

特别是在目前倡导的 Agile 开发和测试流程中,开发和测试的周期都很短,有效的覆盖率信息能够帮助测试人员更加准确地控制测试结果和周期、跟踪问题,保证软件正常发布。

 

回页首

EMMA 在测试执行中的应用

在这一部分将逐步介绍 EMMA 在功能测试过程中的使用过程和步骤。为了使整个介绍过程容易理解,在文章附录中提供了示例程序,文章中通过对示例程序进行操作介绍使用 EMMA 的命令。

插装被测组件

EMMA 通过对被测组件进行插装来跟踪被测组件的执行过程,因此对被测组件进行插装是使用 EMMA 获得覆盖率信息的第一步。测试人员应首先和开发人员讨论,确定哪一部分包含了符合插装要求的文件( Java 文件),哪一部分需要考虑覆盖率信息,然后选择合适的方式进行插装。

  • 插装准备

在执行插装操作之前,首先应该扩展 Java 虚拟机,即将 emma.jar 放到被测组件运行使用的JRE 目录下面作为 JRE 的扩展,以便 EMMA 能够被调用。 emma.jar 包含了 EMMA 核心功能模块的实现和 EMMA 运行时所需的类文件,这些文件是使用 EMMA 所必需的。

由于示例被测组件运行在 Websphere Portal Server 中,并使用默认的 JRE 运行,因此将emma.jar 放到 “/opt/WebSphere/PortalServer/java/jre/lib/ext” 下面。在实际的测试中,将该路径进行相应的替换。

  • 插装

EMMA 中提供了 “instr” 命令完成插装操作。插装操作可以面向 JAR 包、 WAR 包、 WAR 包、类文件和目录,选择合适的命令进行插装可以使插装过程变得简便。下面的1-4通过具体例子介绍了不同情况下的插装命令。

  1. 插装目录和类文件

对于类文件,通过指定类文件所在的目录实现。

清单 1. 对类文件插装命令
/opt/WebSphere/PortalServer/java/jre/bin/java emma instr -m overwrite -ip
/opt/WebSphere/PortalServer/installedApps/NumberQuizWEB_10yggsru.ear/NumberQuizWEB.war/WEB
-INF/classes -Dmetadata.out.file=/root/emma/Number_coverage.em

EMMA: processing instrumentation path
EMMA: instrumentation path processed in 682 ms
EMMA: [5 class(es) instrumented, 0 resource(s) copied]
EMMA: metadata merged into [/root/emma/Number_coverage.em] {in 72 ms}
  1. 插装 JAR 包

JAR 包可以作为一个整体进行插装。通过对整个 JAR 进行插装,可以避免对 JAR 包进行解压和压缩的过程,提高插装效率。

清单 2. 对 JAR 包插装命令
/opt/WebSphere/PortalServer/java/jre/bin/java emma instr -m overwrite -cp  TestWs.jar -
Dmetadata.out.file=/root/emma/Number_coverage.em

EMMA: processing instrumentation path
EMMA: instrumentation path processed in 675 ms
EMMA: [7 class(es) instrumented, 4 resource(s) copied]
EMMA: metadata merged into [/root/emma/Number_coverage.em] {in 60 ms}
  1. 插装 WAR/EAR 包

由于 WAR/EAR 包需要运行在特定的环境中,所以在进行插装之前,需要先将其安装在特定的 J2EE 容器中,然后将其看作目录进行插装。

清单 3. 对 WAR/EAR 包插装命令
/opt/WebSphere/PortalServer/java/jre/bin/java emma instr -m overwrite  \
-ip NumberQuizWEB.war -Dmetadata.out.file=/root/emma/Number_coverage.em

EMMA: processing instrumentation path
EMMA: instrumentation path processed in 610 ms
EMMA: [5 class(es) instrumented, 0 resource(s) copied]
EMMA: metadata merged into [/root/emma/Number_coverage.em] {in 94 ms}
  1. 选择性的插装

EMMA 支持对整个 JAR 包和目录进行插装,但如果在 JAR 包或者目录中包含系统的文件或者测试过程中不关心的文件时,应该进行选择性插装,因为这些文件的存在会影响测试结果的百分比。 EMMA 提供了选择插装的选项,实现选择性插装。

清单 4. 选择插装命令
/opt/WebSphere/PortalServer/java/jre/bin/java emma instr -m overwrite -cp  TestWs.jar
-ix +org.wstest.service.* -Dmetadata.out.file=/root/emma/Number_coverage.em

EMMA: processing instrumentation path
EMMA: instrumentation path processed in 637 ms
EMMA: [4 class(es) instrumented, 6 resource(s) copied]
EMMA: metadata merged into [/root/emma/Number_coverage.em] {in 107 ms}

上述命令选择了与清单2中同样的 JAR 包,由于只包含了 org.wstest.service.* 内的内容,因此只插装了4个类。

以上的1-4分别介绍了在插装过程中的常用命令,下面对命令中用到的一些参数进行解释。

参数 “m”代表插装后文件输出的模式。有三个值可供选择: “copy” ,“overwrite” 和 “fullcopy” 。其中,“copy” 和 “ fullcopy” 这两种模式将会改变插装文件所在的目录,并需要测试人员手动为其生成所需的包,使用起来比较复杂。“overwrite” 模式直接用插装后的文件覆盖插装前文件,使用方便。但是由于同一时间生成的文件只能插装一次,在 “overwrite”模式下,插装前的文件已经丢失,测试人员无法重复插装操作,因此建议在插装之前先将需要插装的文件和包进行备份。

参数 “ip” 和 “cp” 用来提供插装路径,其中 “cp” 用来指明一个文件夹, “ip” 指定单独的文件或者 JAR 包。

参数 “Dmetadata.out.file” 用来指定插装得到的元数据文件保存的路径。

EMMA 中通过 “ix” 参数指定文件的包含和排除关系,其中在 “+” 符号后的文件为包含进的文件, “-” 后面的内容为排除在外的文件。

  • 合并元数据

完成插装操作以后,在指定的路径下会产生一些名为 “*coverage.em” 的文件,这些文件保存了插装的元信息,这些信息主要是记录插装过程中的插装点在被测代码中的位置。如果在插装过程中,指定这些文件到同一文件的话, EMMA 默认将元数据进行合并。如果测试人员未指定路径,或者希望得到独立的元文件,这些文件将分别产生在默认或指定的目录下。测试人员还可以通过使用 “merge” 命令手动将这些元文件进行合并,保证生成的覆盖率报表的全面性。注意:合并操作不支持逆向操作。

清单 5. 合并元数据命令
/opt/WebSphere/PortalServer/java/jre/bin/java emma merge
-input <path1>/coverage1.em,<path2>/coverage.em -out <path>/coverage2.em

在 “input” 后面的参数为待合并的文件名,在 “out” 后面的参数为合并以后的结果文件。

完成上面的操作以后,就已经完成了收集覆盖率信息的准备工作。接下来测试人员可以进行正常的测试工作,在运行测试的过程中, EMMA 将跟踪并记录执行轨迹,得到覆盖率信息。

运行测试用例,得到覆盖率报告

完成插装工作以后,测试人员可以按照测试计划运行测试用例。 EMMA 将在测试执行的过程中记录代码执行信息并将结果记录在内存中。每次当 JVM 停止时,内存中记录的执行信息将被清除并被保存到 “*.ec” 的文件中。但是在实际测试的过程中, JVM 的停止很难控制,因此测试人员可以定时手动将内存中执行信息写出。在这种情况下,内存中的记录被输出,但是内存中的内容不被清除。清单5-7介绍了收集覆盖率信息以及生成覆盖率信息报告的命令。

清单 6. 从远程机器上收集覆盖率信息
/opt/WebSphere/PortalServer/java/jre/bin/java -cp emma.jar emma ctl –connect
 auscsdpfvtvm15.bto.ibm.com:47653 -command coverage.dump,/root/emma/Number_coverage.ec

EMMA: processing control command sequence
EMMA: executing [coverage.dump (/root/emma/Number_coverage.ec,,true)]
EMMA: coverage.dump: runtime coverage data remotely merged into
                          [/root/emma/Number_coverage.ec] {in 83 ms}
EMMA: coverage.dump: command completed in 96 ms
EMMA: control command sequence complete
清单 7. 从本地收集覆盖率信息
/opt/WebSphere/PortalServer/java/jre/bin/java -cp emma.jar emma ctl -connect
auscsdpfvtvm15.bto.ibm.com:47653 -command coverage.get,/root/emma/Number_coverage.ec

EMMA: processing control command sequence
EMMA: executing [coverage.get (/root/emma/Number_coverage.ec,true,true)]
EMMA: coverage.get: local copy of coverage data merged into
          [/root/emma/Number_coverage.ec] {in 39 ms}
EMMA: coverage.get: command completed in 79 ms
EMMA: control command sequence complete

这样收集到的信息被保存在 “coverage.ec” 中, “coverage.ec” 是二进制格式的文件,因此很难被用来查看覆盖率结果。

清单 8. 生成覆盖率报告
/opt/WebSphere/PortalServer/java/jre/bin/java -cp emma.jar emma report -r html -in
 /root/emma/Number_coverage.em,/root/emma/Number_coverage.ec -
Dreport.html.out.file=/root/emma/Number_coverage.html -Dreport.metrics=class:80
(,method:75)

EMMA: processing input files
EMMA: 2 file(s) read and merged in 42 ms
EMMA: writing [html] report to [/root/emma/coverage/Number_coverage.xml]

在生成覆盖率报告的过程中,测试人员可以根据测试要求通过 “Dreport.metrics” 参数设定满意的覆盖率标准。在示例命令中设定了类覆盖率的满意度为80%。

测试报告可以以 HTML ,文本和 XML 三种格式输出。图1、图2为 HTML 格式的报告的例子。覆盖率的报告是以包、类、方法三级单位组织的。图1是 Index 类的执行情况,其中红颜色代表该覆盖率未达到满意的覆盖率标准。图2则是包 org.numberquiz 中 QuizBran 类的执行情况,从总体看,类覆盖率为100%,方法为91%。在附录中可以看到示例程序完整的测试覆盖率报告。

图 1. Index 测试报告

图 2. QuizBran 测试报告

在功能测试过程中,为每个单独的测试用例生成独立的覆盖率报告能够给测试过程带来很大的帮助:

  • 当测试用例失败或者抛出异常时,可以通过覆盖率报告找到该测试用例对应的代码,这样就可以为测试人员提供可能出错代码的范围。这一报告不仅可以帮助测试人员在提交问题时更加详细的描述错误,提供更详细的信息,还可以为开发人员跟踪问题提供线索,缩短解决问题的周期。
  • 测试人员可以从独立的测试报告中获得代码和功能模块的对应关系,更好的理解测试用例的作用。
  • 独立的测试报告可以帮助测试人员改进测试用例的设计,删除重复的测试用例,将覆盖点较多的测试用例进行拆分。

为得到独立的测试报告,需要在每次执行测试用例前,将内存中的执行信息清除。目前有两种方法支持清除记录,测试人员可在测试过程中,根据需要选择合适的方法。

  • 每次运行完一个测试用例,重启 JVM 。这种方法能够完整的清除内存中记录的执行信息,但是每次重启 JVM 给测试带来很多麻烦。
  • 使用 “coverage.reset” 命令,该命令可以在不重启 JVM 的情况下,清除内存中记录的方法、块、行的执行信息,但是无法清除类覆盖信息。如果用户关注的重点在方法的覆盖信息上,可以选择这种方法。
清单 9. 清除内存中覆盖率信息命令
/opt/WebSphere/PortalServer/java/jre/bin/java -cp emma.jar emma ctl -connect
auscsdpfvtvm15.bto.ibm.com:47653 -command coverage.reset

EMMA: processing control command sequence
EMMA: executing [coverage.reset ()]
EMMA: coverage.reset: coverage reset for 5 classes {in 0 ms}
EMMA: coverage.reset: command completed in 31 ms
EMMA: control command sequence complete

合并覆盖率结果

完成所用的测试用例后,测试覆盖信息可以被合并在一起,得到整个测试的覆盖报告。覆盖率结果文件通过 “merge” 命令合并 “*.ec” 文件实现的。

另外,由于 EMMA 中测试覆盖率是通过与 “*.em” 文件关联获得代码信息的,因此当代码发生变化时,已经运行过的测试不必完全重复,只需将得到的 “*.ec” 文件合并(新得到的 “*.ec” 文件放在后面),然后关联最新的 “*.em” 文件即可得到代码变化后的覆盖率信息,这方便了 EMMA 支持版本变化的测试。在生成新的测试报告的时候,需要注意 “*.ec” 的时间一定要晚于 “*.em” 文件。

清单 9. 合并覆盖率结果命令
/opt/WebSphere/PortalServer/java/jre/bin/java emma merge
–input coverage1.ec,coverage2.ec,coverage3.ec –output coverage.ec

如果在生成测试报告的时候,如果出现 “com.vladium.emma.EMMARuntimeException: [CLASS_STAMP_MISMATCH] runtime version of class in the coverage data is not consistent with the version of this class in the metadata, possibly because stale metadata is being used for report generation” 错误,说明在生成新的 “*.em” 前后代码曾经被修改过,并且被修改的代码所在的类文件在新的测试中没有被覆盖到,这就需要重新执行这部分测试,保证修改过的部分被重新执行。

 

回页首

使用覆盖率报告总结和评估测试过程

到目前为止,针对每个测试用例的测试覆盖率报告和测试整体的覆盖率报告都已经得到了。这些报告可以帮助测试人员总结和分析测试结果,改进测试设计。

目前的功能测试中,测试人员主要借助执行的测试用例数目和测试过程中的问题报告来评价测试过程,因此,测试用例的设计直接关系到测试的充分性。测试人员往往无法从目前的测试结果报告中得到哪些部分被覆盖了,哪些部分未被覆盖的信息,也就造成了在测试结束时测试人员对测试结果没有信心。另外,很多情况下,测试人员完成了某些特定情况的测试,异常情况往往被忽视。

覆盖率报告为测试人员查看测试覆盖情况提供了清晰的视图。尽管100%的覆盖率不能证明没有问题,但是它为测试人员提供了参考。当覆盖率很低的时候,测试人员可以通过覆盖率报告的提示找到原因。

与开发人员讨论测试结果

在覆盖率结果、测试用例设计和执行文档的指导下,测试人员可以更清楚地与开发人员讨论测试结果,可以更加清楚的发现:

  • 测试人员忽略的部分。
  • 测试用例设计中被覆盖而测试执行中未覆盖的部分。
  • 程序中的无用代码。

例如:在实际的测试过程中,发现一个叫 syslog.messages 包的覆盖率一直很低,一些类和方法始终没有被覆盖到。在测试执行过程中,与这些类相关的结果也没有出现。因此,在与开发人员确认后,发现对这些内容的调用在写代码的时候被遗忘了(图3中红色框中的内容)。

图 3. 覆盖率报告分析

改进测试设计

利用覆盖率报告,测试人员可以改进测试用例的设计:

  • 移除覆盖范围重复的测试用例。
  • 对于覆盖点过多的测试用例,可以进行拆分,保证测试用例具有针对性。
  • 对于测试中未覆盖的部分,增加测试用例保证测试完整性。
 

回页首

结论

在功能测试中,测试人员一般不能直接获得被测对象的源代码,类、方法覆盖率可以作为衡量和评估测试的重要标准。 EMMA 在字节码插装的基础上捕获并报告测试中的覆盖率信息,使覆盖率成功地用于功能测试。通过在功能测试中使用 EMMA 可以改进测试设计,帮助问题分析,并能更好地评估测试,保证测试质量。

代码覆盖率工具 EMMA的更多相关文章

  1. C++开源代码覆盖率工具OpenCppCoverage介绍(Windows)

    关于代码覆盖率统计工具,Linux平台下,gcc内置支持gcov,通过编译时加参数选项,进行代码插桩,从而实现代码覆盖率.在Windows平台下,早在几年前,我还没找到特别好用又开源的覆盖率工具,所以 ...

  2. C++代码覆盖率工具Coverage Validator

    市面上的C++代码覆盖率工具大都收费,Coverage Validator也不例外.Coverage Validator应该少有人听过,我也是在stackoverflow里听别人介绍的.所以下载了试用 ...

  3. 单元测试-代码覆盖率工具 -- JaCoCo

    最近学习Mybatis的官方文档,看到了[项目文档]一节有很多内容没有见过,做个笔记,理解一下. 随着敏捷开发的流行,编写单元测试已经成为业界共识.但如何来衡量单元测试的质量呢?有些管理者片面追求单元 ...

  4. JaCoCo&#160;代码覆盖率工具(基于Maven+TestNG)

    JaCoco是一个代码覆盖率库. 官方网站:http://www.jacoco.org/ 安装: 以 Maven(http://www.testclass.net/maven/) 安装为例: < ...

  5. Python 代码覆盖率统计工具 coverage.py

    coverage.py是一个用来统计python程序代码覆盖率的工具.它使用起来非常简单,并且支持最终生成界面友好的html报告.在最新版本中,还提供了分支覆盖的功能. 官方网站: http://ne ...

  6. oracle存储过程代码覆盖率统计工具

    目前针对于高级语言如C++,JAVA,C#等工程都有相关的代码覆盖率统计工具,但是对于oracle存储过程或者数据库sql等方面的项目,代码覆盖率统计和扫描工具相对较少. 因此针对这种情况,设计了代码 ...

  7. Jacoco远程统计代码覆盖率

    Jacoco   什么是Jacoco? Jacoco是一个开源的Java代码覆盖率工具,Jacoco可以嵌入到Ant .Maven中,并提供了EclEmma Eclipse插件,也可以使用JavaAg ...

  8. Jacoco远程统计tomcat服务(Windows系统)的代码覆盖率

    Jacoco远程统计tomcat服务(Windows系统)的代码覆盖率 2017-09-21 目录 1 Jacoco的安装和设置  1.1 什么是Jacoco?  1.2 Jacoco安装  1.3 ...

  9. maven

    maven常见问题问答 1.前言 Maven,发音是[`meivin],"专家"的意思.它是一个很好的项目管理工具,很早就进入了我的必备工具行列,但是这次为了把project1项目 ...

随机推荐

  1. theano scan optimization

    selected from Theano Doc Optimizing Scan performance Minimizing Scan Usage performan as much of the ...

  2. MapReduce 单词统计案例编程

    MapReduce 单词统计案例编程 一.在Linux环境安装Eclipse软件 1.   解压tar包 下载安装包eclipse-jee-kepler-SR1-linux-gtk-x86_64.ta ...

  3. Android错误

    1. [2016-09-16 14:25:45 - X_Card] Found 2 versions of android-support-v4.jar in the dependency list, ...

  4. poj 1724ROADS(bfs和dfs做法)

    /* dfs比较好想,就是测试数据的问题,导致在遍历边的时候要倒着遍历才过! */ #include<iostream> #include<cstdio> #include&l ...

  5. Json 基于jQuery+JSON的省市联动效果

    helloweba.com 作者:月光光 时间:2012-09-12 21:57 标签: jQuery  JSON  Ajax  省市联动     省市区联动下拉效果在WEB中应用非常广泛,尤其在一些 ...

  6. Effective C++ -----条款08: 别让异常逃离析构函数

    析构函数绝对不要吐出异常.如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序. 如果客户需要对某个操作函数运行期间抛出的异常作出反应,那么class应 ...

  7. Golang redigo hmset hset 问题

    最近公司项目,换到了golang 下面来开发,遇到了redis存储链表的问题,困扰了我好几天,后面静下心来,好好读了一下源码,发现官方的例子,最终还是羊毛出在羊身上 c, err := dial() ...

  8. 关于JQ的$.deferred函数。参考网络文档

    由于jQuery版本问题对Deferred对象的实现有所不同,具体请参照jQuery api:   jQuery.Deferred()基于Promises/A规范实现,因为jQuery本身的设计风格, ...

  9. MVC学习(四)几种分页的实现(2)

    在第一种分页方式中,仅仅实现了分页,但并未有体现出MVC的优势,没有体现出泛型编程思想,尤其在数据量很大的时候,分页十分缓慢,除此之外,还没有实现很好的封装,不是一个通用方法. 因此,我希望只要传入数 ...

  10. Play on Words[HDU1116]

    Play on Words Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)To ...