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是安装在本机的,这个时候就需要配置环境变量:

UT覆盖率 java 代码覆盖率 jacoco_java


UT覆盖率 java 代码覆盖率 jacoco_代码覆盖率_02


随后在cmd中输入命令 ant -version

UT覆盖率 java 代码覆盖率 jacoco_代码覆盖率_03


到这里就表示ant的安装已经完成了。

(2) 在tomcat中加入jacoco

打开tomcat的bin目录下的catalina.bat文件,在JAVA_OPTS参数中加入一行。

UT覆盖率 java 代码覆盖率 jacoco_java_04


脚本代码如下:

set JAVA_OPTS=-server -Xms1024m -Xmx1024m -XX:PermSize=512M -XX:MaxNewSize=512m -XX:MaxPermSize=512m -Djava.awt.headless=true -javaagent:D:\AutoTest\jacoco\lib\jacocoagent.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的文件的地址,以及其他的一些配置即可。

UT覆盖率 java 代码覆盖率 jacoco_java_05


UT覆盖率 java 代码覆盖率 jacoco_代码覆盖率_06


1) address:目标的jacoco的ip地址

2) port:目标的jacoco开放的端口号

3) retryCount:将尝试建立连接的重试次数。 这可以用于等待目标JVM成功启动。

4) dump:是否转储执行数据。

5) reset:执行导出后是否重置数据

6) destfile:生成的数据的存放位置

7) append:如果设置为 true,执行数据文件已存在,覆盖率数据追加到现有文件中。如果设置为 false,现有执行数据文件将被替换。

(2) report

report命令,需要知道exec文件的位置,以及源代码、类的位置,还有生成报告的格式需要指定。

UT覆盖率 java 代码覆盖率 jacoco_tomcat_07


当涉及到多个源码包和类的时候,建议使用group的方式区分开来。

UT覆盖率 java 代码覆盖率 jacoco_java_08


导出格式的选择。

UT覆盖率 java 代码覆盖率 jacoco_UT覆盖率 java_09

(4)完整脚本
bulid.xml中的完整代码如下:

<?xml version="1.0" ?>
<project name="wftestReport" xmlns:jacoco="antlib:org.jacoco.ant" default="jacoco">
    <!--Jacoco的安装路径-->
    <property name="jacocoantPath" value="D:\AutoTest\jacoco\lib\jacocoant.jar"/>

    <!--最终生成.exec文件的路径,Jacoco就是根据这个文件生成最终的报告的-->
    <property name="jacocoexecPath" value="D:\AutoTest\JRES\codeCoverage\mergedwftest.exec"/>

    <!--生成覆盖率报告的路径,直接放在tomct下面,外界直接访问-->
    <property name="reportfolderPath" value="D:\AutoTest\JRES\codeCoverage\report"/>

    <!--远程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:\AutoTest\JRES\mysrc\src\main\java\"/>


    <!--.class文件路径-->
    <!--跑的是class,标注的是源码?-->
    <!--<property name="checkOrderClasspath" value="/root/wftest/target/classes/com/hundsun" />-->

    <property name="express_class" value="D:\AutoTest\JRES\report\WEB-INF\classes\com\hundsun\reportXml\expression\"/>

    <!--合并报告路径-->
    <property name="mergePath" value="D:\AutoTest\JRES\codeCoverage" />



    <!--让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:\AutoTest\JRES\codeCoverage\merged.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:\AutoTest\JRES\codeCoverage\report.csv" />         
        </jacoco:report>
  </target>
</project>

(5)脚本使用

以上内容全部放在一个build.xml中,如图所示:

UT覆盖率 java 代码覆盖率 jacoco_java_10


在这个目录下进行cmd命令执行,

UT覆盖率 java 代码覆盖率 jacoco_java_11


访问目标tomcat服务,并生成.exec文件。

接下来,我们可以执行命令ant report ,来生成代码覆盖报告。

UT覆盖率 java 代码覆盖率 jacoco_UT覆盖率 java_12


到此jacoco已经生成了代码覆盖率的报告,路径如下图所示:

UT覆盖率 java 代码覆盖率 jacoco_代码覆盖率_13

4. 报告解读

(1)颜色

没有覆盖:在这一行中没有分支被执行(红色方块)

部分覆盖:这一行的分支中只有一部分被执行(黄色方块)

完全覆盖:这一行的所有分支都被执行(绿色方块)

UT覆盖率 java 代码覆盖率 jacoco_代码覆盖率_14


数量越多,进度条越长;红色越多,覆盖的越少。

 指令覆盖

 分支覆盖

 行覆盖

 方法覆盖

 类覆盖