jacoco统计自动化代码覆盖率
1. 简介
1.1. 什么是Jacoco
Jacoco是一个开源的代码覆盖率工具,可以嵌入到Ant 、Maven中,并提供了EclEmma Eclipse插件,也可以使用JavaAgent技术监控Java程序。很多第三方的工具提供了对Jacoco的集成,如sonar、Jenkins等。
1.2. 什么是代码覆盖率
代码覆盖(Code coverage)是软件测试中的一种度量,描述程式中源代码被测试的比例和程度,所得比例称为代码覆盖率。
代码覆盖率是衡量测试质量的一个重要指标。在对一个软件产品进行了单元测试、组装测试、集成测试以及接口测试等繁多的测试之后,我们能不能就此对软件的质量产生一定的信心呢?这就需要我们对测试的质量进行考察。如果测试仅覆盖了代码的一小部分,那么不管我们写了多少测试用例,我们也不能相信软件质量是有保证的。相反,如果测试覆盖到了软件的绝大部分代码,我们就能对软件的质量有一个合理的信心。
代码覆盖分为下面五种情况:
1.2.1. 函数覆盖
函数覆盖(Function Coverage),执行到程序中的每一个函数(或副程式)。
1.2.2. 语句覆盖
语句覆盖(Statement Coverage),又称行覆盖(Line Coverage),段覆盖(Segment Coverage),基本块覆盖(Basic Block Coverage),这是最常用也是最常见的一种覆盖方式,就是度量被测代码中每个可执行语句是否被执行到了。这里说的是“可执行语句”,因此就不会包括像C++的头文件声明,代码注释,空行,等等。非常好理解,只统计能够执行的代码被执行了多少行。需要注意的是,单独一行的花括号{}也常常被统计进去。语句覆盖常常被人指责为“最弱的覆盖”,它只管覆盖代码中的执行语句,却不考虑各种分支的组合等等。假如你的上司只要求你达到语句覆盖,那么你可以省下很多功夫,但是,换来的确实测试效果的不明显,很难更多地发现代码中的问题。
1.2.3. 判断覆盖
判断覆盖(Decision Coverage),又称分支覆盖(Branch Coverage),所有边界覆盖(All-Edges Coverage),基本路径覆盖(Basic Path Coverage),判定路径覆盖(Decision-Decision-Path)。它度量程序中每一个判定的分支是否都被测试到了。这句话是需要进一步理解的,应该非常容易和下面说到的条件覆盖混淆。因此我们直接介绍第三种覆盖方式,然后和判定覆盖一起来对比,就明白两者是怎么回事了。
1.2.4. 条件覆盖
条件覆盖(Condition Coverage),它度量判定中的每个子表达式结果true和false是否被测试到了。
1.2.5. 路径覆盖
路径覆盖(Path Coverage),又称断言覆盖(Predicate Coverage)。它度量了是否函数的每一个分支都被执行了。 这句话也非常好理解,就是所有可能的分支都执行一遍,有多个分支嵌套时,需要对多个分支进行排列组合,可想而知,测试路径随着分支的数量指数级别增加。
1.3. Jacoco的功能
1.3.1. 覆盖率计数器
Jacoco使用一系列的不同的计数器来做覆盖率的度量计算。所有这些计数器都是从java的class文件中获取信息,这些class文件可以(可选)包含调试的信息在里面。即使在没有源码的情况下,这种方法也可以实时有效地对应用程序进行度量和分析。在大部分情况下,收集到的信息可以映射到源码,可视化到每一行代码的粒度。但这种方法还是有一些限制。这些class文件必须使用调试信息来编译,这样才可以计算行的覆盖率和提供出源码的高亮。但不是所有的JAVA语言的结构都可以直接编译成一致的二进制代码。在这种情况下,java 编译器会创建所谓的“合成”代码,会导致产生一些不期望得到的覆盖率结果。
1.3.2. 指令覆盖率
Jacoco最小的计数单元是单个java二进制代码指令。指令覆盖率提供了代码是否被执行的信息。这个度量完全独立源码格式,并且总是可用,即使class文件里面没有调试信息。
1.3.3. 分支覆盖率
Jacoco也计算分支的覆盖率,包括所有的if和switch语句。这个度量计算一个方法里面的总分支数,确定执行和不执行的分支数量。分支覆盖率总是可用的,即使class文件里面没有调试信息。注意异常处理是不在分支度量里面统计的。
2. Jacoco的安装
(1)安装ant
首先需要安装ant和jacoco。
下载完apache-ant的压缩包后,解压,放到常用的软件安装路径下即可。ant是基于java的,所以要先确保系统中已经安装了jdk。比如我的ant是安装在本机的,这个时候就需要配置环境变量:
随后在cmd中输入命令 ant -version
到这里就表示ant的安装已经完成了。
(2) 在tomcat中加入jacoco
打开tomcat的bin目录下的catalina.bat文件,在JAVA_OPTS参数中加入一行。
脚本代码如下:
set JAVA_OPTS=-server -Xms1024m -Xmx1024m -XX:PermSize=512M -XX:MaxNewSize=512m -XX:MaxPermSize=512m -Djava.awt.headless=true -javaagent:D:AutoTestjacocolibjacocoagent.jar=includes=com.hundsun.*,output=tcpserver,port=8229,address=127.0.0.1 -Xverify:none
参数说明如下:
1) -javaagent: 的后面跟jacoco的安装路径
2) includes= 选项,选择你要覆盖率的服务,也就是包名
3) port=,选择你要打开的端口,jacoco的端口,与所对应的tomcat端口不能一样,与其他端口也不能冲突
4) address= tomcat服务所在机器的ip地址(如果想在跟tomcat服务同一台机器上执行ant任务的话,需要改为127.0.0.1)
5) -Xverify:none,避免启动报错的情况
这样配置后就将jacoco嵌入到了tomcat中,到时候tomcat起来后,就通过开放的端口,来访问jacoco检测到的数据。正常启动tomcat,jacoco就在实时监测tomcat中运行的war包,此时数据全部存放在内存中。
3. ant脚本
(1) dump数据
远程的从另外一个jvm上获取数据,并且不用停止应用的运行,这个是非常有用的。但是目标jvm需要把jacoco配置为tcpserver模式,目前使用的,也就是在tomcat的语句中,output格式为tcpserver。
Dump数据,首先需要知道jacoco的位置和端口,然后导出exec的文件的地址,以及其他的一些配置即可。
1) address:目标的jacoco的ip地址
2) port:目标的jacoco开放的端口号
3) retryCount:将尝试建立连接的重试次数。 这可以用于等待目标JVM成功启动。
4) dump:是否转储执行数据。
5) reset:执行导出后是否重置数据
6) destfile:生成的数据的存放位置
7) append:如果设置为 true,执行数据文件已存在,覆盖率数据追加到现有文件中。如果设置为 false,现有执行数据文件将被替换。
(2) report
report命令,需要知道exec文件的位置,以及源代码、类的位置,还有生成报告的格式需要指定。
当涉及到多个源码包和类的时候,建议使用group的方式区分开来。
导出格式的选择。
(4)完整脚本
bulid.xml中的完整代码如下:
<?xml version="1.0" ?> <project name="wftestReport" xmlns:jacoco="antlib:org.jacoco.ant" default="jacoco"> <!--Jacoco的安装路径--> <property name="jacocoantPath" value="D:AutoTestjacocolibjacocoant.jar"/> <!--最终生成.exec文件的路径,Jacoco就是根据这个文件生成最终的报告的--> <property name="jacocoexecPath" value="D:AutoTestJREScodeCoveragemergedwftest.exec"/> <!--生成覆盖率报告的路径,直接放在tomct下面,外界直接访问--> <property name="reportfolderPath" value="D:AutoTestJREScodeCoverage eport"/> <!--远程tomcat服务的ip地址--> <property name="server_ip" value="127.0.0.1"/> <!--前面配置的远程tomcat服务打开的端口,要跟上面配置的一样--> <!--这个端口有别于tomcat的端口,相当于是嵌在tomcat里的监视器--> <property name="server_port" value="8229"/> <!--源代码路径--> <!--<property name="checkOrderSrcpath" value="/root/LoginDemo4/src/main/java/" />--> <!--可以配置多个源代码--> <property name="express_src" value="D:AutoTestJRESmysrcsrcmainjava"/> <!--.class文件路径--> <!--跑的是class,标注的是源码?--> <!--<property name="checkOrderClasspath" value="/root/wftest/target/classes/com/hundsun" />--> <property name="express_class" value="D:AutoTestJRES eportWEB-INFclassescomhundsun eportXmlexpression"/> <!--合并报告路径--> <property name="mergePath" value="D:AutoTestJREScodeCoverage" /> <!--让ant知道去哪儿找Jacoco--> <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml"> <classpath path="${jacocoantPath}" /> </taskdef> <!--dump任务: 根据前面配置的ip地址,和端口号, 访问目标tomcat服务,并生成.exec文件。--> <target name="dump"> <jacoco:dump address="${server_ip}" reset="false" destfile="${jacocoexecPath}" port="${server_port}" append="true"/> </target> <target name="merge"> <!--将路径下的exec文件全部合并,并存放到destfile中--> <jacoco:merge destfile="D:AutoTestJREScodeCoveragemerged.exec"> <fileset dir="${mergePath}" includes="*.exec"/> </jacoco:merge> </target> <!--jacoco任务: 根据前面配置的源代码路径和.class文件路径, 根据dump后,生成的.exec文件,生成最终的html覆盖率报告。--> <target name="report"> <!--暂时不删除,一旦删除,其他两个的报告也没了--> <delete dir="${reportfolderPath}" /> <mkdir dir="${reportfolderPath}" /> <jacoco:report> <executiondata> <file file="${jacocoexecPath}" /> </executiondata> <structure name="ucf report"> <group name="express related"> <classfiles> <fileset dir="${express_class}" /> </classfiles> <sourcefiles encoding="utf-8"> <fileset dir="${express_src}" /> </sourcefiles> </group> </structure> <html destdir="${reportfolderPath}" encoding="utf-8" /> <csv destfile="D:AutoTestJREScodeCoverage eport.csv" /> </jacoco:report> </target> </project>
(5)脚本使用
以上内容全部放在一个build.xml中,如图所示:
在这个目录下进行cmd命令执行,
访问目标tomcat服务,并生成.exec文件。
接下来,我们可以执行命令ant report ,来生成代码覆盖报告。
到此jacoco已经生成了代码覆盖率的报告,路径如下图所示:
4. 报告解读
(1)颜色
没有覆盖:在这一行中没有分支被执行(红色方块)
部分覆盖:这一行的分支中只有一部分被执行(黄色方块)
完全覆盖:这一行的所有分支都被执行(绿色方块)
数量越多,进度条越长;红色越多,覆盖的越少。
指令覆盖
分支覆盖
行覆盖
方法覆盖
类覆盖