生成jacoco报告_java项目代码覆盖率统计工具jacoco原理

Jacoco全称是java code coverage,顾名思义它是一个开源的覆盖率工具,它针对的开发语言是java,其使用方法很灵活,可以嵌入到Ant、Maven中。

代码覆盖率的意义

1.了解测试情况

测试过程中覆盖和未覆盖的地方,可能存在的风险。分析未覆盖代码,反推在测试设计是否充分,进一步明确测试设计阶段的问题。

2.发现测试死角、冗余代码、历史废弃代码

有助于发现多个测试用例都覆盖不到的代码,收集方法覆盖率,为废弃的代码提供依据。

3.度量自动化用例

为自动化用例提供覆盖率统计情况,分析覆盖率报告,完善自动化用例。

4.精准回归

构建代码调用关系,精准的确定回归测试范围,避免了全量回归造成测试资源的浪费。

覆盖率统计的指标

Jacoco包含了多种尺度的覆盖率计数器,包含指令级覆盖(Instructions,C0coverage),分支(Branches,C1coverage)、圈复杂度(CyclomaticComplexity)、行覆盖(Lines)、方法覆盖(non-abstract methods)、类覆盖(classes)。

行覆盖率:度量被测程序的每行代码是否被执行,判断标准行中是否至少有一个指令被执行。

类覆盖率:度量计算class类文件是否被执行。

分支覆盖率:度量if和switch语句的分支覆盖情况,计算一个方法里面的总分支数,确定执行和不执行的 分支数量。

方法覆盖率:度量被测程序的方法执行情况,是否执行取决于方法中是否有至少一个指令被执行。

指令覆盖:计数单元是单个java二进制代码指令,指令覆盖率提供了代码是否被执行的信息,度量完全 独立源码格式。

圈复杂度:在(线性)组合中,计算在一个方法里面所有可能路径的最小数目,缺失的复杂度同样表示测 试案例没有完全覆盖到这个模块。

Jacoco工作原理

cd52f9b0fdee326adafe71eff0cac555.png

这个图包含了几种不同的收集覆盖率信息的方法,每种方法的实现方法都不一样,标有旗帜图标的部分是jacoco比较有特色的地方。

Code Coverage代码覆盖率
Runtime Profiling运行时分析
JVMPIJava虚拟机监视程序接口
JVMTIJava虚拟机工具接口
Instrumentation注入
Source源码注入
Byte Code字节码注入
Offline离线模式
On-The-Fly在线模式
Replace替换方式
Inject注入方式
Class Loader类加载
Java Agent代理

上表红色部分为jacoco支持的,详细的解释一下: 

  1. jacoco在Byte Code时使用的ASM技术修改字节码方法,可以修改Jar文件、class文件字节码文件。

  2. JaCoCo同时支持on-the-fly和offline的两种插桩模式。

On-the-fly插桩

JVM中通过-javaagent参数指定特定的jar文件启动Instrumentation的代理程序,代理程序在通过Class Loader装载一个class前判断是否转换修改class文件,将统计代码插入class,测试覆盖率分析可以在JVM执行测试代码的过程中完成。

Offline模式

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

On-the-fly和offline比较

  1. On-the-fly模式更方便简单进行代码覆盖分析,无需提前进行字节码插桩,无需考虑classpath 的设置。

  2. 存在如下情况不适合on-the-fly,需要采用offline提前对字节码插桩:

  3. 运行环境不支持java agent。

  4. 部署环境不允许设置JVM参数。

  5. 字节码需要被转换成其他的虚拟机如Android Dalvik VM。

  6. 动态修改字节码过程中和其他agent冲突。

  7. 无法自定义用户加载类。

JaCoCo执行最小的java版本

最小需要Java1.5

字节码处理方式

JaCoCo通过注入来修改和生成java字节码,使用的是ASM库。

那么什么是ASM库呢?

ASM 库是一款基于 Java 字节码层面的代码分析和修改工具。ASM 可以直接生产二进制的 class 文件,也可以在类被加载入 JVM 之前动态修改类行为。

java方法控制流分析

jacoco是如何在字节码注入的?

先看一段java程序:

public static void example() {    a();    if(cod()) {        b();    } else {        c();    }    d();}

编译转换成字节码后,内容如下:

public static example()V    INVOKESTATIC a()V    INVOKESTATIC cond()z    IFEQ L1    INVOKESTATIC b()V    GOTO L2L1:INVOKESTATIC c()VL2:INVOKESTATIC d()VRETURN

我们知道jacoco是字节码注入方式,它是通过一个Probe探针的方式来注入的,具体如下:

探针是字节指令集插入到java方法中,程序执行后可以被记录,它不会改变原有代码的行为。

我们看看探针前后插入比较:

4821bfc41f441f422e62445411c82a1c.png

黄颜色的部分就是探针注入的地方。

JaCoCo是根据控制流Type来采用不同的探针插入策略的。

一个用java字节码定义的java方法的控制流图可能有以下的type,每一个type连接一个源指令与目标指令,type不同探针的注入策略也会不同,如下是type定义:

Type

Source

Target

ENTRY--First instruction in method
SEQUENCEInstruction,exceppt GOTO,xRETURN,THROWSubsequent instruction
JUMPGOTO,IFx,TABLESWITCH or LOOKUPSWITH instructionTarget instruction
EXHANDLERAnt instruction in handler scopeTarget instruction
EXITxRETURN or THROW instruction--
EXEXITAny instruction--

探针不改变该方法的行为,但记录他们已被执行的事实,从理论上讲,可以在控制流图的每一个边插入一个探针,作为探针实现本身需要多个字节码指令,这将增加几倍的类文件的大小和执行速度。

以上是jacoco插桩原理,如果想深入了解,可以去看看它的源码实现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值