使用 EMMA 测量测试覆盖率

2024-01-17 10:48

本文主要是介绍使用 EMMA 测量测试覆盖率,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

2006年10月13日 18:57:00
EMMA 是一个用于检测和报告 JAVA 代码覆盖率的开源工具。它不但能很好的用于小型项目,很方便得得出覆盖率报告,而且适用于大型企业级别的项目。

EMMA 有许多优点,首先你能免费得到它,并把它用于自己项目的开发。它支持许多种级别的覆盖率指标:包,类,方法,语句块(basic block)和行,特别是它能测出某一行是否只是被部分覆盖,如条件语句短路的情况。它能生成 text,xml,html 等形式的报告,以满足不同的需求,其 html 报告提供下钻功能,我们能够从 package 开始一步步链接到我们所关注的某个方法。EMMA 能和 Makefile 和 Ant 集成,便于应用于大型项目。特别还须指出的一点是,EMMA 的效率很高,这对于大型项目来说很重要。

EMMA 是通过向 .class 文件中插入字节码的方式来跟踪记录被运行代码信息的。EMMA 支持两种模式:On the fly 和 Offline 模式。

On the fly 模式往加载的类中加入字节码,相当于用 EMMA 实现的 application class loader 替代原来的 application class loader。

Offline 模式在类被加载前,加入字节码。

On the fly 模式比较方便,缺点也比较明显,如它不能为被 boot class loader 加载的类生成覆盖率报告,也不能为像 J2EE 容器那种自己有独特 class loader 的类生成覆盖率报告。这时,我们能求助于 Offline 模式。

EMMA 也支持两种运行方式:Command line 和 Ant。

命令行一般和 On the fly 模式一起适用,对于简单的项目能够快速产生覆盖率报告。通过 Ant task 来运行 EMMA 的话,特别适用于大型的项目。

本文后面提供的实例主要是演示如何集成 EMMA 和 Ant,通过 Offline 模式产生覆盖率报告。





回页首


示例项目

示例工程 SampleProject 是个小型的项目,有一个类 NumberParser,主要功能是把一个字符串解析成 float 型。下面是整个工程的目录结构。


图1. 示例项目的目录结构
图1. 示例项目的目录结构

下面,我们开始来为我们的工程编写 Ant 脚本。


清单1设置一些属性,包括源文件,二进制文件,JUnit 报告,覆盖率报告等的路径
	
>!-设置Java类被注入字节码后存放的路径--<
>property name="bin.instrument.dir" location="../instrbin" /<
>!-设置覆盖率元数据和报告的路径--<
>property name="coverage.dir" location="../coverage" /<
>!--设置junit报告的路径 --<
>property name="junitReport.dir" location="../junitReport" /<
>!-设置主题代码bin路径--<
>property name="bin.main.dir" location="../srcbin" /<
>!-设置测试代码bin路径--<
>property name="bin.test.dir" location="../testbin" /<
>!--设置主题代码源路径--<
>property name="src.main.dir" location="../../SampleProject/src" /<
>!--设置测试代码源路径--<
>property name="src.test.dir" location="../../SampleProjectTest/test"
/<
>!-指示需要注入字节码的Java类的路径--<
>path id="classpath.main"<
>pathelement location="${bin.main.dir}" /<
>/path<
>!-指示 emma.jar 和emma_ant.jar 的路径--<
>path id="emma.lib"<
>pathelement location="${libs}/emma.jar" /<
>pathelement location="${libs}/emma_ant.jar" /<
>/path<
>!-允许emma--<
>property name="emma.enabled" value="true" /<

其中目录${ bin.instrument.dir }存放被注入字节码的类,"emma.lib" 指向 emma 资源所在的位置。


清单2为 ANT 定义 EMMA 任务
	>!-为ANT添加EMMA任务--<
>taskdef resource="emma_ant.properties" classpathref="emma.lib" /<


清单3编译源代码和测试代码
		>target name="compile-src.main"<
>mkdir dir="${bin.main.dir}" /<
>javac destdir="${bin.main.dir}" debug="on"<
>src path="${src.main.dir}" /<
>/javac<
>copy todir="${bin.main.dir}"<
>fileset dir="${src.main.dir}"<
>exclude name="**/*.java" /<
>/fileset<
>/copy<
>/target<

>target name="compile-src.test"<
>mkdir dir="${bin.test.dir}" /<
>javac destdir="${bin.test.dir}" debug="on"<
>src path="${src.test.dir}" /<
>classpath location="${bin.main.dir}" /<
>/javac<
>copy todir="${bin.test.dir}"<
>fileset dir="${src.test.dir}"<
>exclude name="**/*.java" /<
>/fileset<
>/copy<
>/target<

编译分两阶段,先编译源代码,然后再编译测试用例代码。


清单4在所要测试类的代码中插入字节码
		>!-对编译在路径bin.main.dir中的Java类注入字节码, 
并且把注入字节码的新Java类存放到路径bin.instrument.dir--<
>!-覆盖率的元数据存放在路径coverage.dir中--<
>target name="instrument"<
>mkdir dir="${bin.instrument.dir}" /<
>mkdir dir="${coverage.dir}" /<
>emma enabled="${emma.enabled}"<
>instr instrpathref="classpath.main"
destdir="${bin.instrument.dir}"
metadatafile="${coverage.dir}/metadata.emma"
merge="true"<
>/instr<
>/emma<
>copy todir="${bin.instrument.dir}"<
>fileset dir="${bin.main.dir}"<
>exclude name="**/*.java" /<
>/fileset<
>/copy<
>/target<

当${emma.enabled}为 true 时,才生成插入字节码的类。>instr <中指定了要 instrument 的类的地址, instrumented 后类存放的地址,以及 metadata 存放的地址。< font>


清单5运行测试用例,得到一些生成报告的元数据
	
>!-执行测试用例同时生成junit测试报告和emma代码覆盖率报告--<
>target name="test"<
>mkdir dir="${junitReport.dir}" /<
>junit fork="true" forkmode="once"
printsummary="withOutAndErr"
errorproperty="test.error"
showoutput="on"<
>!-指明代码覆盖率的元数据的存放位置--<
>jvmarg
value="-Demma.coverage.out.file=${coverage.dir}/metadata.emma" /<
>jvmarg value="-Demma.coverage.out.merge=true" /<
>classpath location="${bin.instrument.dir}" /<
>classpath location="${bin.test.dir}" /<
>classpath refid="emma.lib" /<

>formatter type="xml" /<
>!-执行所有以Test结尾的junit测试用例--<
>batchtest todir="${junitReport.dir}" haltonfailure="no"<
>fileset dir="${bin.test.dir}"<
>include name="**/*Test.class" /<
>/fileset<
>/batchtest<
>/junit<

>/target<

在运行测试用例前,需要设置 jvmarg。所有的测试用例都跑在 instrumented 的类上面。


清单6生成 JUnit 报告
	
>target name="gen-report-junit"<
>!-生成junit测试报告--<
>junitreport todir="${junitReport.dir}"<
>fileset dir="${junitReport.dir}"<
>include name="*" /<
>/fileset<
>report format="frames" todir="${junitReport.dir}" /<
>/junitreport<
>/target<


清单7生成覆盖率报告
		>!-生成代码覆盖率报告--<
>target name="gen-report-coverage"<
>!-如果属性emma.enabled的值是true,就生成代码覆盖率报告 --<
>emma enabled="${emma.enabled}"<
>report sourcepath="${src.main.dir}"
sort="+block,+name,+method,+class"
metrics="method:70,block:80,line:80,class:100"<
>fileset dir="${coverage.dir}"<
>include name="*.emma" /<
>/fileset<
>html outfile="${coverage.dir}/coverage.html"
depth="method" columns="name,class,method,block,line" /<
>/report<
>/emma<
>/target<

>report <中 sourcepath 指明源代码所在的位置,以便能够显示每行代码的覆盖情况。sort指明生成列表的排列顺序,"+"表示升序,"-"表示降序。metrics 可为每个度量指明一个覆盖率阈值,若未达到该阈值,则该行会被标记出来(前提是报告的形式支持这个功能,如 html)。>html <指明以 html 形式生成报告,depth 指明报告的详细程度,columns 指明生成列表列名的排列顺序。< font>





回页首


显示报告

我们已经写好了Ant脚本,接下来你就可以运行该脚本了。这里假设你已经搭好了运行 Ant 和 JUnit 的环境,直接到脚本所在目录,在命令行敲入 Ant 即可。

下面是各个层次的报告:


图2整个项目层次的报告
图2整个项目层次的报告

图3包层次的报告
图3包层次的报告

图4类层次的报告
图4类层次的报告

图5用颜色标记的源代码
图5用颜色标记的源代码

你会发现有三种颜色,绿色,红色和黄色,它们分别表示该行:被测试到,未被测试到,以及部分被测试到。红色或黄色的部分是需要引起你注意的,bug 也许就隐藏在这部分代码中,你所需做的就是设计一些测试用例,使它们运行以前未被执行到的语句。如上面那张图给出了我们一些信息,String 中含有"+"号的情况未被测试到,还有"isPositive"只被测试到 true 或 false 的一种情况,你需要相应的增加一些测试用例。运行新加的测试用例,你也许会发现一些新的 bug,并修正这些 bug。





回页首


隐藏在报告背后的问题

对于这个简单的例子,你会发现,我们很容易达到 100% 的测试覆盖率,你也许会松口气说:啊,我把所有情况都测试到了,这下放心了。在这里很遗憾的告诉你,EMMA 的功能是有限的,它不支持决策覆盖和路径覆盖。事实上,对于一个稍复杂的工程进行穷尽的测试是不可能的。


清单8决策覆盖和路径覆盖的代码示例
	
/**
* Parses the given string to a float number
*
* @param number
* the given string
* @return the float number related with the string
*
* @throws IllegalArgumentException
* if the string is empty, null or can not parse to a float
*/
public float parse(String number) {
if (number.equals("")||number == null ) {
throw new IllegalArgumentException(
"Number string should not be empty or null");
}
StringIterator stringIterator = new StringIterator(number);
getSign(stringIterator);
int integer = getInteger(stringIterator);
float fraction = getFraction(stringIterator);
float total = integer + fraction;
return isPositive ? total : (-1) * total;
}


清单9决策覆盖和路径覆盖的测试用例
    
public void test_parse () {
NumberParser np = new NumberParser();
String number ="";
try {
np.parse(number);
fail("should throw IAE");
} catch (IllegalArgumentException e) {
// pass
}
number = "22.010";
float parsedNumber = np.parse(number);
assertEquals((float) 22.010, parsedNumber);

number = "-22.010";
parsedNumber = np.parse(number);
assertEquals((float) 22.010, parsedNumber);

}

运行 Ant 脚本,生成报告,你会发现,测试用例都运行通过了,测试覆盖报告也表明代码所有的行都被执行到了。但细心的读者肯定早已看到上面代码存在 Bug。若传进 parse 的 string 为 null 的话,并不是如我们所愿,得到 IllegalArgumentException,而是抛出了 NullPointerException。

虽然下面那行是绿色的,但它只表明每个条件语句都被执行到了,并不能说明每个条件都取到true和false两种情况。在我们设计的测试用例中, "null == number"只取到 false 一种情况。我们需要在我们的测试用例中加入对 string 情况是 null 的测试。


图6 决策覆盖和路径覆盖率报告
图6 决策覆盖和路径覆盖率报告

清单10 修正代码的 Bug
                if (null == number || "".equals(number)) {





回页首


结束语

为你的项目生成覆盖率报告,EMMA 是个不错的选择。通过覆盖率报告,我们能发现并修复一些隐藏的 bug,我们的软件会变得更强壮

来源于IBM网站: http://www-128.ibm.com/developerworks/cn/java/j-lo-emma/index.html



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1333548


这篇关于使用 EMMA 测量测试覆盖率的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/615775

相关文章

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在