本文主要讨论如何用emma给Java Application做代码覆盖,区别于java单元测试的code coverage, Java Application一直处于运行状态,所以不能暴力的强行kill进程来收集代码覆盖数据,所以本文梳理下网络上的相关文章,整理下过程,并记录下尝试过程中一些常见问题的解决:
基本步骤分为五步:
(1)修改编译选项:
将编译的选项设置为debug启用,同时要注意debug的level.例如maven compiler默认就启用了debug但是没有将debug的level都设置进去,所以这里注意设置启用和启用的级别。例如对于maven项目中的compile plugin: (如果使用了progard进行混淆,则去掉混淆)
<configuration>
<debug>true</debug>
<debuglevel>lines,source</debuglevel>
</configuration>
(2) 注入:
注入coverage信息,可以使用-ix来过滤指定包:
java emma instr -m overwrite -cp server.jar -ix +com.server* -Dmetadata.out.file=coverage.em
note:
2.1 其中在 “+” 符号后的文件为包含进的文件, “-” 后面的内容为排除在外的文件,例如
-ix +com.foo.*,-com.foo.test.*,-com.foo.*Test*
2.2 要支持EMMA,需要复制emma.jar到lib目录,记得修改权限,例如:jdk1.7.0_60/jre/lib/ext/emma.jar
2.3 有时候加上过滤条件导出并没有生效,显示过滤的文件少了,但是产生的coverage.em基本没有变,可以删掉原先的,估计有merge操作。
(3)修改应用启动的jvm参数并启动应用:
3.1 修改启动应用的jvm参数:
-Demma.rt.control=true
为什么要增加这项,可以查看代码:默认是不启动实时收集功能。
IProperties appProperties = getAppProperties();
if (appProperties != null) {
String property = appProperties.getProperty("rt.control", "false");
enableController = Property.toBoolean(property);
}
if (enableController) {
int port = 47653;
if (appProperties != null) {
String property = appProperties.getProperty("rt.control.port");
if (property != null) {
try {
port = Integer.parseInt(property);
} catch (NumberFormatException ignore) {
System.err
.println("ignoring malformed [rt.control.port] value: "
+ property);
}
ps: 还存在其他很多参数,例如-Demma.coverage.out.file=/var/coverage.ec控制输出文件,-Demma.rt.control.port=12345,用来控制端口
3.2 启动,并查看log来确认是否正常:
应该存在2行log:其中第二行对应3.1要解决的问题。
EMMA: collecting runtime coverage data ...
EMMA: runtime controller started on port [47653]
其中,如果启动失败并提示class format错误,这是应用是JDK1.7编译的,而之前注入使用的emma的注入方式应该是不兼容的,所以可以通过增加JVM启动参数来解决:
1.7使用-XX:-UseSplitVerifier 如果是1.8使用-Xverify:none
(4)执行测试用例,然后使用命令收集数据:
java emma ctl -connect localhost:47653 -command coverage.get,coverage.ec
假设需要合并之前的,执行命令:
java emma merge -input coverage1.ec,coverage2.ec -out coverage.ec
PS: 也可以支持em的Merge.
(5)结合(2)产生的em和(4)产生的ec文件以及源代码的路径产生html报告:
java emma report -r html -sp /xxxx/xxxx/src/main/java -in coverage.em,coverage.ec -Dreport.html.out.file=coverage.html
这里需要注意的是源代码要和产生的统计信息一致,否则假设源代码当中的一个文件已经重命名,那么在html报告中还是提示找不到源文件。
实际测试中,我们还会应用单元测试,产生覆盖率,然后可以与上述产生的覆盖率合并。-in 支持传多个ec,em.
对于单元测试如何产生覆盖数据,可以直接配置EMMA的maven插件,然后使用mvn emma:emma来产生在target目录:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-XX:-UseSplitVerifier</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>emma-maven-plugin</artifactId>
<version>1.0-alpha-3</version>
</plugin>
PS:
通过上面可知,对于Coverage数据的过滤只有在注入阶段,在报告阶段并没有,好在本人同事对emma.jar进行了二次开发(emma_rar),支持在上面的第五步Report阶段过滤Class/Package。
具体命令:
java emma report -r html -props filter.txt -sp /xxxx/xxxx/src/main/java -in coverage.em,coverage.ec -Dreport.html.out.file=coverage.html
filter.txt 文件内容的格式可以是report.html.filter.class或者report.html.filter.package,例如示例:
report.html.filter.class=com.Tp.java,com.Constants.java //多个类用,分割。
这里一定要注意的是:
(1)如果是内部类的过滤,一定不要带上外部类的名字,比如不应该是outer.inner.java,而应该是inner.java.这个可以查看EM文件的内容获知。
(2)支持正则表达式。
Notes:
(1) debug log: -Dverbosity.level=trace1
以上即为整个过程的记录以备忘!