Jacoco代码覆盖率学习笔记记录

jacoco学习

一、覆盖率计数器
1. 行覆盖

所有类文件均携带debug信息编译,则每行的覆盖率可计算。当至少一个指令被指定到源码行且已执行时,该源码行被认为已执行。
全部未覆盖:该行中指令均未执行,红色标志
部分覆盖:该行中部分指令执行,黄色标志
全覆盖:该行中所有指令已执行,绿色标志

2. 类覆盖

当类中至少有一个方法已执行,则该类被认为已执行。Jacoco中认为构造函数和静态初始化方法也当作被执行过的方法。Java接口类型若包含静态初始化方法,这种接口也被认为是可执行的类。

3. 方法覆盖

每个非抽象方法至少包含一个指令。当至少一个指令被执行,该方法被认为已执行。由于Jacoco基于字节码级别的,构造函数和静态初始化方法也被当作方法计算。其中有些方法,可能无法直接对应到源码中,比如默认构造器或常量的初始化命令。

4. 分支覆盖

Jacoco为if和switch语句计算分支覆盖率。这个指标计算一个方法中的分支总数,并决定已执行和未执行的分支的数量。分支覆盖率在class文件中缺少debug信息时也可使用。异常处理不在分支覆盖的统计范围内。
全部未覆盖:所有分支均未执行,红色标志
部分覆盖:只有部分分支被执行,黄色标志
全覆盖:所有分支均已执行,绿色标志

5. 指令覆盖

Jacoco计数的最小单元是Java字节码指令,它为执行/未执行代码提供了大量的信息。这个指标完全独立于源格式,在类文件中缺少debug信息时也可以使用。

6. 圈复杂度

Jacoco对每个非抽象方法计算圈复杂度,总结类、包、组的复杂性。
圈复杂度:在(线性)组合中,计算在一个方法里面所有可能路径的最小数目。所以复杂度可以作为度量单元测试是否有完全覆盖所有 场景的一个依据。在没有debug信息的时候也可以使用。
圈复杂度V(G)是基于方法的控制流图的有向图表示:V(G) = E - N + 2
E是边界数量,N是节点数量。
Jacoco基于下面方程来计算复杂度,B是分支数量,D是决策点数量:
V(G) = B - D + 1
基于每个分支的被覆盖情况,Jacoco也为每个方法计算覆盖和缺失的复杂度。缺失复杂度同样表示测试案例没有完全覆盖到这个模块。注意Jacoco不将异常处理作为分支,try/catch块也同样不增加复杂度。

二、Jacoco原理

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

1. on-the-fly模式

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

2. offline模式

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

on-the-fly和offline对比

on-the-fly更方便简单,无需提前插桩,无需考虑classpath设置问题。
以下情况不适合使用on-the-fly模式:
(1)不支持javaagent
(2)无法设置JVM参数
(3)字节码需要被转换成其他虚拟机
(4)动态修改字节码过程和其他agent冲突
(5)无法自定义用户加载类

Java方法的控制流分析

官方文档在这里:https://www.jacoco.org/jacoco/trunk/doc/flow.html

1. 探针插入策略

探针可以在现有指令之间插入附加指令,他们不改变已有方法行为,只是去记录是否已经执行。可以认为探针放置在控制流图的边缘上,理论上讲,我们可以在控制流图的每个边缘插入一个探针,但这样会增加类文件大小,降低执行速度。事实上,我们每个方法只需要一些探针,具体取决于方法的控制流程。
如果已经执行了探测,我们知道已经访问了相应的边缘,从这个边缘我们可以得出其他前面的节点和边:
(1)如果访问了边,我们知道该边的源节点已经被执行。
(2)如果节点已经被执行且节点是一个边缘的目标节点,则我们知道已经访问了该边。

img

上述探针插入策略没有考虑到隐式异常,如果两个探针之间的控制流被未使用throw的语句显示创建的异常终端,则其间的所有指令都被视为未覆盖。因此,只要后续行包含至少一个方法调用,Jacoco就会在两行的指令间添加额外的探测。该方法仅使用于有debug信息的编译的类文件。且不考虑除方法调用之外的其他指令的隐式异常。

2. 探针的实现

探针需要满足如下几点要求:
(1)记录执行
(2)识别不同的探针
(3)线程安全
(4)对应用程序无影响
(5)最小的运行时开销
Jacoco给每个类一个boolean[]数组实例,每个探针对应该数组中的一个条目。无论何时执行,都用下面4条字节码指令将条目设置为true。

ALOAD    probearray
xPUSH    probeid
ICONST_1
BASTORE
三、jacoco的简单使用
1.JaCoCo

​ Jacoco从多种角度对代码进行了分析,包括指令(Instructions,C0 Coverage),分支(Branches,C1 Coverage),圈复杂度(Cyclomatic Complexity),行(Lines),方法(Methods),类(Classes)。

2.springboot工程,jacoco单元测试报告获取

注意:想要获取覆盖率结果,工程里必须写单测代码,否则不会有结果

  1. 在pom中添加jacoco插件
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.6</version>
<configuration>
  <!--指定生成 .exec 文件的存放位置-->
  <destFile>target/coverage-reports/jacoco-unit.exec</destFile>
  <!--Jacoco 是根据 .exec 文件生成最终的报告,所以需指定 .exec 的存放路径-->
  <dataFile>target/coverage-reports/jacoco-unit.exec</dataFile>
</configuration>
<executions>
  <execution>
  	<id>jacoco-initialize</id>
  	<goals>
  		<goal>prepare-agent</goal>
  	</goals>
  </execution>
  <execution>
  	<id>jacoco-site</id>
  	<phase>test</phase>
  	<goals>
  		<goal>report</goal>
  	</goals>
  </execution>
</executions>
</plugin>
  1. 执行mvn test命令
    在target目录下生成site目录,里面有index.html文件,就是生成的jacoco报告,打开就可以查看覆盖率报告

img

  1. 浏览器打开index.html页面

    img

三、jacoco报告详解

  1. Instructions
    Jacoco计算的最小单位就是字节码指令。指令覆盖率表明了在所有的指令中,哪些被执行过以及哪些没有被执行。这项指数完全独立于源码格式并且在任何情况下有效,不需要类文件的调试信息。

  2. Branches
    Jacoco对所有的if和switch指令计算了分支覆盖率。这项指标会统计所有的分支数量,并同时指出哪些分支被执行,哪些分支没有被执行。这项指标也在任何情况都有效。异常处理不考虑在分支范围内。
    在有调试信息的情况下,分支点可以被映射到源码中的每一行,并且被高亮表示。

img

红色背景:无覆盖,该行的所有指令均无执行。
黄色背景:部分覆盖,该行部分指令被执行。
绿色背景:全覆盖,该行所有指令被执行。

参考博客:https://blog.csdn.net/sayoko06/article/details/99312875

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值