本文主要讨论如何用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
以上即为整个过程的记录以备忘!