1、覆盖率工具对比
根据网上的资料搜索发现,现在常用的 java 单元测试覆盖率工具主要有:
-
- Emma
- Cobertura
- Jacoco
- Clover(商用)
工具 | Jacoco | Emma | Cobertura |
原理 | 使用 ASM 修改字节码 | 修改 jar 文件,class 文件字节码文件 | 基于 jcoverage,基于 asm 框架对 class 文件插桩 |
覆盖粒度 | 行,类,方法,指令,分支 | 行,类,方法,基本块,指令,无分支覆盖 | 项目,包,类,方法的语句覆盖/分支覆盖 |
插桩 | on the fly、offline | on the fly、offline | offline,把统计代码插入编译好的class文件中 |
生成结果 | 在 Tomcat 的 catalina.sh 配置 javaangent 参数,指出需要收集覆盖率的文件,shutdown 时才收集,只能使用 kill 命令关闭 Tomcat,不要使用 kill -9 | html、xml、txt,二进制格式报表 | html,xml |
缺点 | 需要源代码 | 1、需要 debug 版本,并打来 build.xml 中的 debug 编译项; 2、需要源代码,且必须与插桩的代码完全一致 | 1、不能捕获测试用例中未考虑的异常; 2、关闭服务器才能输出覆盖率信息(已有修改源代码的解决方案,定时输出结果;输出结果之前设置了 hook,会与某些服务器的 hook 冲突,web 测试中需要将 cobertura.ser 文件来回 copy |
性能 | 快 | 小巧 | 插入的字节码信息更多 |
执行方式 | maven,ant,命令行 | 命令行 | maven,ant |
Jenkins 集成 | 生成 html 报告,直接与 hudson 集成,展示报告,无趋势图 | 无法与 hudson 集成 | 有集成的插件,美观的报告,有趋势图 |
报告实时性 | 默认关闭,可以动态从 jvm dump 出数据 | 可以不关闭服务器 | 默认是在关闭服务器时才写结果 |
维护状态 | 持续更新中 | 停止维护 | 停止维护,不支持java1.8的lamda表达式 |
指令覆盖:Jacoco计算的最小单位就是字节码指令。指令覆盖率表明了在所有的指令中,哪些被指令过以及哪些没有被执行。这项指数完全独立于源码格式并且在任何情况下有效,不需要类文件的调试信息。
行覆盖率:度量被测程序的每行代码是否被执行,判断标准行中是否至少有一个指令被执行。
红色背景:无覆盖,该行的所有指令都没有执行
黄色背景:部分覆盖,该行的部分指令被执行
绿色背景:全覆盖,该行的所有指令被执行
类覆盖率:度量计算class类文件是否被执行。
分支覆盖率:度量if和switch语句的分支覆盖情况,并且计算一个方法里的总分支数,确定执行和不执行的分支数量。
其中在有调试信息
根据上述对比,本次调研选用 jacoco 来进行单元测试覆盖率的工具
2、jacoco单元测试覆盖率工具使用步骤如下:
1、添加插件
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.7</version>
<configuration>
<excludes>
<!-- 这里配置我们想要排除的包路径-->
<exclude>com/tm/test/*</exclude>
<!-- 通过通配符来排除指定格式的类-->
<exclude>**/*Tests.class</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<!--定义输出的文件夹-->
<outputDirectory>target/jacoco-report</outputDirectory>
<!--执行数据的文件-->
<dataFile>${project.build.directory}/jacoco.exec</dataFile>
<!--要从报告中排除的类文件列表,支持通配符(*和?)。如果未指定则不会排除任何内容-->
<!-- <excludes>**/test/*.class</excludes>-->
<!--包含生成报告的文件列表,支持通配符(*和?)。如果未指定则包含所有内容-->
<!-- <includes></includes>-->
<!--HTML 报告页面中使用的页脚文本。-->
<!-- <footer></footer>-->
<!--生成报告的文件类型,HTML(默认)、XML、CSV-->
<formats>HTML</formats>
<!--生成报告的编码格式,默认UTF-8-->
<outputEncoding>UTF-8</outputEncoding>
<!--抑制执行的标签-->
<!-- <skip></skip>-->
<!--源文件编码-->
<sourceEncoding>UTF-8</sourceEncoding>
<!--HTML报告的标题-->
<title>${project.name}</title>
</configuration>
</execution>
</executions>
</plugin>
2、对要测试代码编写单元测试用例
要测试代码
public class MessageBuilder {
public String getMessage(String name) {
StringBuilder result = new StringBuilder();
if (name == null || name.trim().length() == 0) {
result.append("empty");
} else {
result.append("Hello+").append(name);
}
return result.toString();
}
}
单元测试用例,写在test/java 目录下
import org.junit.Assert;
import org.junit.Test;
public class MessageBuilderTest {
MessageBuilder messageBuilder = new MessageBuilder();
@Test
public void testGetMessage() throws Exception {
String result = messageBuilder.getMessage("name");
Assert.assertEquals("Hello+name", result);
}
@Test
public void testGetMessage2() throws Exception {
String result = messageBuilder.getMessage("");
Assert.assertEquals("empty", result);
}
}
3、执行单元测试,获取覆盖率
可以这样
或者直接命令行执行 mvn clean test
执行结果显示在target/jacoco-report 下,可以 使用浏览器打开 index.html查看覆盖率情况
可以看到部分代码未覆盖成功
增加测试用例
@Test
public void testGetMessage3() {
String a =null;
String result = messageBuilder.getMessage(a);
Assert.assertEquals("empty", result);
}
重新执行可以看到覆盖率已经达到 100%
此外,jacoco 还可以指定对那些类进行单元测试覆盖,哪些进行排除
当前其他主流代码覆盖率工具
不同语言有不同的代码覆盖率工具,大家在选择的时候最好选择那种社区活跃度高、开源和网上评论好的工具。
另:需要思考的是,需要使用 jacoco 代理方式实现无侵入式的覆盖率统计,增量代码覆盖率等