jacoco统计覆盖率最佳实践

jacoco总结

JaCoCo是面向Java的开源代码覆盖率工具,JaCoCo以Java代理模式运行,它负责在运行测试时检测字节码。 JaCoCo会深入研究每个指令,并显示每个测试过程中要执行的行。 为了收集覆盖率数据,JaCoCo使用ASM即时进行代码检测,并在此过程中从JVM Tool Interface接收事件,最终生成代码覆盖率报告。

jacoco运行有离线(offline)、在线(on the fly)模式之说,所谓在线模式就是在应用启动时加入jacoco agent进行插桩,在开发、测试人员使用应用期间实时地进行代码覆盖率分析。相信很多的java项目开发人员并不会去写单元测试代码的,因此覆盖率统计就要把手工测试或接口测试覆盖的情况作为重要依据,显然在线模式更符合实际需求,本文以在线模式为例进行演示。

Jacoco安装

从官网
https://www.jacoco.org/jacoco/下载最新版本并解压到指定目录

代码覆盖率介绍

覆盖率是用来衡量测试代码对功能代码的测试情况。通过统计测试代码中对功能代码中行、分支、类等模拟场景数量,来量化说明测试的充分度。

代码覆盖率=代码的覆盖程度,一种度量方式。

覆盖率简单说:跑了一个测试用例,项目代码中的那些模块、文件、类、方法 执行了。

其中行覆盖率是最细颗粒度,其他覆盖率都可从行覆盖率情况统计算出来。

行覆盖

当至少一个指令被指定源码行执行时,该源码行被认为已执行。

通俗的说就是,测试行为,触发了这某一行代码,则这一行代码就被覆盖了。

分支覆盖

if 和switch 语句算作分支覆盖率,这个指标计算一个方法中的分支总数,并决定已执行和未执行的分支的数量。

全部未覆盖:所有分支均未执行,红色标志

部分覆盖:部分分支被执行,黄色标志

全覆盖:所有分支均已执行,绿色标志。

方法覆盖

当方法中至少有一个指令被执行,该方法被认为已执行,包括构造函数和静态初始化方法。

覆盖率的误区

若代码如下:

if (i>100)
    j = 10/i        // 没有除0错误
else
    j = 10/(i+2)    // i==-2,除0错误

覆盖2个分支,只需要设计i101 和 i1,但是对于找到i==-2这个bug点时没有作用的。

所以:

1、不要简单的追求高的代码覆盖率

2、高覆盖率测试用例不等于测试用例有效

3、没覆盖的分支相当于该分支上的任何错误肯定都找不到

代码覆盖率意义

分析未覆盖部分的代码,反推测试设计是否充分,没有覆盖到的代码是否存在测试设计盲点。

覆盖率报告解析

img

  • Instructions: Java 字节指令的覆盖率。执行的最小单位,和代码的格式无关。

  • Branches: 分支覆盖率。注意,异常处理不算做分支。

  • Cxty(Cyclomatic Complexity): 圈复杂度, Jacoco 会为每一个非抽象方法计算圈复杂度,并为类,包以及组(groups)计算复杂度。

    圈复杂度简单的说就是为了覆盖所有路径,所需要执行单元测试数量,圈复杂度大说明程序代码可能质量低且难于测试和维护。

  • Lines: 行覆盖率,只要本行有一条指令被执行,则本行则被标记为被执行。

  • Methods: 方法覆盖率,任何非抽象的方法,只要有一条指令被执行,则该方法被计为被执行。

  • Classes: 类覆盖率,所有类,包括接口,只要其中有一个方法被执行,则标记为被执行。注意:构造函数和静态初始化块也算作方法。

类角度相信信息

img

  • 钻石代表分支覆盖情况、背景颜色代表指令覆盖率

  • 红色钻石/背景:这一行没有分支/指令被执行

  • 黄色钻石/背景:这一行中只有部分分支/指令被执行

  • 绿色钻石/背景:这一行的所有分支/指令都被执行

启动jacoco agent进行插桩

jacoco使用插桩的方式来记录覆盖率数据,是通过一个probe探针来注入。jacoco有2种插桩模式

,offline模式和on-the-fly模式。

单元测试offline模式:

对应的是jacoco的offline模式。

offline模式就是在测试之前先对文件进行插桩,生成插过桩的class或jar包,测试插过桩的class和jar包,生成覆盖率信息到文件,最后统一处理,生成报告。

在脱机模式下,可以使用与代理可用的相同财产集配置JaCoCo运行时,除了类文件已经插入指令的include/excludes选项之外。有两种不同的方式来提供配置:

配置文件:如果在类路径选项中提供了文件jacoco-agent.properties,则从该文件加载。文件必须格式化为Java配置文件格式。

系统配置:选项也可以作为Java系统配置提供。在这种情况下,选项必须以“jacoco-agent”为前缀。例如,*.exec文件的位置可以用系统属性“jacoco-angent.destfile”配置。

<project>
  <reporting>
    <plugins>
      <plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <reportSets>
          <reportSet>
            <reports>
              <!-- select non-aggregate reports -->
              <report>report</report>
            </reports>
          </reportSet>
        </reportSets>
      </plugin>
    </plugins>
  </reporting>
</project>

离线调用jacoco有两种方式,第一种是命令行方式调用jacoco的插件,另外一种是直接在maven中配置jacoco插件。

之前项目中曾经调研过其实还有第三种方式,我是在maven调用是开启了jacoco的代理在maven参数中,官方没有写这样用的方式所以不建议。

mvn命令增加参数

在执行mvn命令时,加上“org.jacoco:jacoco-maven-plugin:prepare-agent”参数即可。 示例:

mvn clean test org.jacoco:jacoco-maven-plugin:0.7.3.201502191951:prepare-agent install -Dmaven.test.failure.ignore=true

在pom文件中添加jacoco插件

具体的配置方法如下:

1.添加依賴

<dependency>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.3</version>
</dependency>

2.配置plugins

      <plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <version>0.8.3</version>
        <configuration>
          <includes>
            <include>com/**/*</include>
          </includes>
        </configuration>
        <executions>
          <execution>
            <id>pre-test</id>
            <goals>
              <goal>prepare-agent</goal>
            </goals>
          </execution>
          <execution>
            <id>post-test</id>
            <phase>test</phase>
            <goals>
              <goal>report</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

其中包含(includes)或排除(excludes)字段的值应该是相对于目录/ classes /的编译类的类路径(而不是包名),使用标准通配符语法:

*   Match zero or more characters
**  Match zero or more directories
?   Match a single character

你也可以这样排除一个包和它的所有子包/子包:

<exclude>com/src/**/*</exclude>

这将排除某些包装中的每个课程,以及任何孩子。例如,com.src.child也不会包含在报表中。

也可以在pom中指定筛选规则:

      <plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <version>${jacoco.version}</version>
        <configuration>
          <includes>
            <include>com/src/**/*</include>
          </includes>
          <!-- rules裏面指定覆蓋規則 -->
          <rules>
            <rule implementation="org.jacoco.maven.RuleConfiguration">
              <element>BUNDLE</element>
              <limits>  
                <!-- 指定方法覆蓋到50% -->
                <limit implementation="org.jacoco.report.check.Limit">
                  <counter>METHOD</counter>
                  <value>COVEREDRATIO</value>
                  <minimum>0.50</minimum>
                </limit>
                <!-- 指定分支覆蓋到50% -->
                <limit implementation="org.jacoco.report.check.Limit">
                  <counter>BRANCH</counter>
                  <value>COVEREDRATIO</value>
                  <minimum>0.50</minimum>
                </limit>
                <!-- 指定類覆蓋到100%,不能遺失任何類 -->
                <limit implementation="org.jacoco.report.check.Limit">
                  <counter>CLASS</counter>
                  <value>MISSEDCOUNT</value>
                  <maximum>0</maximum>
                </limit>
              </limits>
            </rule>
          </rules>
        </configuration>
        <executions>
          <execution>
            <id>pre-test</id>
            <goals>
              <goal>prepare-agent</goal>
            </goals>
          </execution>
          <execution>
            <id>post-test</id>
            <phase>test</phase>
            <goals>
              <goal>report</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

此时运行mvn test生成index.html(即覆盖率报告)位置在:

img

也可以指定输出目录:

<execution>
    <id>post-unit-test</id>
    <phase>test</phase>
    <goals>
        <goal>report</goal>
    </goals>
    <configuration>
        <dataFile>target/jacoco.exec</dataFile>
        <outputDirectory>target/jacoco-ut</outputDirectory>
    </configuration>
</execution>

功能测试on-the-fly模式

对应的是jacoco的on-the-fly模式。

JVM通过 -javaagent参数指定jar文件启动代理程序,代理程序在ClassLoader装载一个class前判断是否修改class文件,并将探针插入class文件,探针不改变原有方法的行为,只是记录是否已经执行。

启jacoco命令:

打开cmd,cd到示例代码的target目录,执行如下命令:

基本语法

-javaagent:[yourpath/]jacocoagent.jar=[option1]=[value1],[option2]=[value2]

示例

java -
javaagent:jacocoagent.jar=includes=*,output=tcpserver,port=6300,address=localhost,append=true -jar
demo.jar

关键参数说明:

  • destfile 执行数据的输出文件的路径。
  • append 如果设置为true并且执行数据文件已经存在,则覆盖率数据将附加到现有文件中。如果设置为false,则将替换现有的执行数据文件。
  • includes 应包含在执行分析中的类名列表,意思就是要收集哪些类的覆盖率信息。列表条目由冒号(:)分隔,并且可以使用通配符(*和?)。除了性能优化或技术拐角情况外,通常不需要此选项。
  • excludes 应从执行分析中排除的类名列表。列表条目由冒号(:)分隔,并且可以使用通配符(*和?)。除了性能优化或技术拐角情况外,通常不需要此选项。如果您想从报告中排除类,请相应地配置相应的报告生成工具。与includes相反。
  • exclclassloader 应从执行分析中排除的类加载器名称的列表。列表条目由冒号(:)分隔,并且可以使用通配符(*和?)。如果特殊框架与JaCoCo代码插入冲突,特别是无法访问Java运行时类的类加载器,则可能需要此选项。
  • output 有 4 个值,分别是 file、tcpserver、tcpclient、mbean,默认是 file。使用 file 的方式只有在停掉应用服务的时候才能产生覆盖率文件,而使用 tcpserver 的方式可以在不停止应用服务的情况下下载覆盖率文件,后面会介绍如何使用 dump 方法来得到覆盖率文件。
    • file:在JVM终止时,执行数据被写入本地文件。
    • tcpserver:外部工具可以连接到JVM,并通过套接字连接检索执行数据。可以在虚拟机退出时进行可选的执行数据重置和执行数据转储。
    • tcpclient:在启动时,JaCoCo代理连接到给定的TCP端点。根据请求将执行数据写入套接字连接。可以在虚拟机退出时进行可选的执行数据重置和执行数据转储。
    • none:不做任何产出。
  • address 是 IP 地址,当输出方法为tcpserver时要绑定到的IP地址或主机名,或者当输出方法是tcpclient时要连接到的IP或主机名。在tcpserver模式中,值“*”会使代理接受任何本地地址上的连接。
  • port 是端口(输出方法为tcpserver时要绑定到的端口,或输出方法为tcpclient时要连接到的端口。在tcpserver模式下,端口必须可用,这意味着如果多个JaCoCo代理应在同一台计算机上运行,则必须指定不同的端口。
  • dumponexit 如果设置为真覆盖率,则将在VM关闭时写入数据。只有在指定了文件或输出为tcpserver/tcpclient并且VM终止时连接打开时,才能写入转储。
  • classdumpdir相对于代理看到的所有类文件转储到的工作目录的位置。这对于调试或动态创建的类(例如使用脚本引擎时)非常有用。
dump - 生成exec文件

基于参数

java -jar jacococli.jar dump [--address <address>] --destfile <path> [--help] [--port <port>] [--quiet] [--reset] [--retry <count>]

OptionDescription
--address <address>要连接的主机名或ip地址(默认本地主机)
--destfile <path>要将执行数据写入的文件
--helpshow help
--port <port>要连接的端口(默认值为6300)
--quiet禁止stdout上的所有输出
--reset转储后重置测试目标上的执行数据
--retry <count>重试次数(默认为10)
java -jar jacococli.jar dump --address 127.0.0.1 --port 6300 --destfile ./jacoco-demo.exec
report - 生成报告

基本参数

java -jar jacococli.jar report [<execfiles> ...] --classfiles <path> [--csv <file>] [--encoding <charset>] [--help] [--html <dir>] [--name <name>] [--quiet] [--sourcefiles <path>] [--tabwith <n>] [--xml <file>]
java -jar jacococli.jar report jacoco-demo.exec --classfiles ../../../target/classes --sourcefiles ../../main/java --html http-report --xml report.xml --encoding=utf-8

注意 jacococli.jar 是放到了test下的resource下上述命令。

OptionDescription
<execfiles>要读取的JaCoCo*.exec文件列表
--classfiles <path>Java类文件的位置
--csv <file>CSV报告的输出文件
--encoding <charset>源文件编码(默认使用平台编码)
--helpshow help
--html <dir>HTML报表的输出目录
--name <name>用于此报告的名称
--quiet禁止stdout上的所有输出
--sourcefiles <path>源文件的位置 ,既源码位置
instrument - Java类文件和JAR文件的离线检测。

基本参数

java -jar jacococli.jar instrument [<sourcefiles> ...] --dest <dir> [--help] [--quiet]

OptionDescription
<sourcefiles>源文件的位置 ,既源码位置
--dest <dir>将插入指令的Java类写入的路径
--helpshow help
--quietsuppress all output on stdout
merge - 将多个exec文件合并到一个新的exec文件中。

基本参数

java -jar jacococli.jar merge [<execfiles> ...] --destfile <path> [--help] [--quiet]

OptionDescription
<execfiles>要读取的JaCoCo*.exec文件列表
--destfile <path>将合并的执行数据写入的文件
--helpshow help
--quietsuppress all output on stdout

这个merge参数是讲多个exec整合为一个文件,据我使用经验来说有两方面用途吧,一是讲多个模块的覆盖率信息统计到一起,二是将n次相同模块的测试结果整合统计。都很有用。

示例

java -jar jacococli.jar merge jacoco-demo.exec jacoco-demo1.exec --destfile ./demo.exec
classinfo - 在提供的位置打印有关Java类文件的信息。
java -jar jacococli.jar classinfo [<classlocations> ...] [--help] [--quiet] [--verbose]
OptionDescription
<classlocations>Java类文件的位置
--helpshow help
--quietsuppress all output on stdout
--verboseshow method and line number details
execinfo - 以可读格式打印exec文件内容
java -jar jacococli.jar execinfo [<execfiles> ...] [--help] [--quiet]
OptionDescription
<execfiles>list of JaCoCo *.exec files to read
--helpshow help
--quietsuppress all output on stdout
  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Jacoco是一个用于Java代码覆盖率分析的开源工具。它可以帮助开发团队评估测试用例对代码的覆盖情况,从而提供关于代码质量和测试覆盖率的详细报告。 首先,为了将Jacoco应用于项目中,我们需要在构建工具中配置Jacoco插件。常用的构建工具有Maven和Gradle,两者都支持Jacoco插件。在配置中,我们需要指定Jacoco的启动参数、输出文件的路径等等。 接下来,我们需要确保项目的测试用例充分覆盖到代码的各个分支和逻辑路径。可以通过编写更全面的测试用例或使用自动化测试工具来实现。在这个阶段,我们可以通过运行测试用例来收集代码覆盖率数据。 然后,我们可以生成Jacoco覆盖率报告。Jacoco可以将收集到的数据转化为易读的HTML或XML格式报告,其中包含了代码覆盖率的详细信息,例如每个类、方法和行的覆盖率百分比。这个报告可以帮助开发团队发现哪些部分的代码缺乏测试覆盖,从而优化测试策略。 最后,我们可以将Jacoco覆盖率报告集成到持续集成环境中,例如Jenkins。这样,每次代码发生变更时,都会自动运行测试用例并生成最新的覆盖率报告。通过与其他开发团队成员共享这份报告,可以促进团队合作和代码质量的持续改进。 需要注意的是,Jacoco只能提供代码覆盖率信息,不能保证测试用例的质量和覆盖率的完整性。因此,在使用Jacoco时,我们还需要关注测试用例的编写质量,以确保基于Jacoco的代码覆盖率评估结果是准确可信的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个双鱼座的测开

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值