Jacoco

发布时间 2023-03-23 14:30:13作者: huiyii

原理

  对 Java 字节码进行插桩,在线 和 离线 两种方式。执行测试用例,收集程序执行轨迹信息,将其 dump 到内存。数据处理器结合程序执行轨迹信息和代码结构信息分析生成代码覆盖率报告。将代码覆盖率报告图形化展示出来,如 html、xml 等文件格式。

  它的使用是一种动态插桩和静态插桩混用的一种原理。所谓插桩就是在你编译的文件里面去插入Jacoco的探针。在代码里面插入一些探针,一些监控,不管你是以单元测试还是以什么测试的身份去跑这个代码。这行代码跑完之后,就会被我的探针记录下来,最终就会被收集展示出来,我的代码覆盖情况基本就被展示出来了。这就是它基本的一个原理。

多维度的覆盖率计数器

指令覆盖Instructions、 分支覆盖Branches、圈复杂度Cyclomatic Complexity(Cxty)、行覆盖Lines、方法覆盖Medhods、类覆盖Classes。详见最后的报告解析

插桩方式

分为源代码(Source Code)注入和字节码(Byte Code)注入两大类:

字节码注入又可以分为两大模式:On-The-Fly 注入模式和 Offline 注入模式。

实现:ASM 是一个 Java 字节码操纵框架,能被用来动态生成类或者增强既有类的功能,可以直接产生 class 文件,也可以在类被加载入 JVM 之前动态改变类行为。

img

 

(1)On-the-fly插桩

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

(2)Offline模式

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

环境搭建

下载

官网下载jacoco压缩包 https://www.jacoco.org/jacoco/,解压。得到相关jar包:

  • jacocoagent:运行时启动tcp服务监控代码覆盖,dump出覆盖率数据。启动应用时主要用来插桩的jar

  • jacocoant:jacoco的任务是ant驱动的,所以这个包用来执行jacoco的任务,向tcp服务发送请求。

  • jacococli:导出覆盖率记录的exec文件,生成覆盖率报告。

使用

1、启动web项目时添加javaagent参数

java -javaagent:/tmp/jacoco/lib/jacocoagent.jar=includes=*,output=tcpserver,port=6300,address=localhost,append=true -jar demo-0.0.1-SNAPSHOT.jar

关键参数说明:

  • javaagent:/tmp/jacoco/lib/jacocoagent.jar=includes=*,这个参数就是启用jacoco代理,其中javaagent:/tmp/jacoco/lib/jacocoagent.jar就是下载jacoco解压后的jacocoagent.jar的绝对路径,includes表示对要插桩的包进行过滤,*代表所有的class都要进行插桩,也可以根据情况进行过滤,如includes=com.mycompany.*

  • output=tcpserver,这里不需要改动,表示以TCP Server方式启动应用并插桩

  • port=6300,Jacoco开启的TCP Server的端口

  • address=localhost,对外开放的地址,也可以指定IP地址

  • demo-0.0.1-SNAPSHOT.jar,就是示例代码构建后target目录生成的jar包

2、测试web项目:接口,功能都可。

3、生成覆盖率报告

# 生成覆盖率文件jacoco.exec
java -jar /tmp/jacoco/lib/jacococli.jar dump --address localhost --port 6300 --destfile /tmp/jacoco/mydemo/jacoco.exec

关键参数说明:

  • --destfile /tmp/jacoco/mydemo/jacoco.exec,其中/tmp/jacoco/mydemo/jacoco.exec为生成exec文件名

  • 其他参数和上一步类似,不再特别说明,注意需要更新jacococli.jar的绝对路径

# 生成html报告report/index.html
java -jar /tmp/jacoco/lib/jacococli.jar report /tmp/jacoco/mydemo/jacoco.exec --classfiles /root/project/RuoYi-Vue/ruoyi-admin/target/classes --sourcefiles /root/project/RuoYi-Vue/ruoyi-admin/src/main/java --html /tmp/jacoco/mydemo/report

关键参数说明:

参数说明:

  • /tmp/jacoco/mydemo/jacoco.exec,表示要解析的exec文件路径

  • --classfiles /root/project/RuoYi-Vue/ruoyi-admin/target/classes,需要指定生成的classes文件目录(制定字节码文件目录)

  • --sourcefiles /root/project/RuoYi-Vue/ruoyi-admin/src/main/java,需要指定源码的文件目录

  • --html /tmp/jacoco/mydemo/report,html报告目录

4、打开 index.html 查看覆盖率报告

 

 

覆盖率指标说明

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

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

    • Missed Instructions覆盖率100%,但分支覆盖率不为100%; 原因:所有代码行都覆盖并不代表所有分支都覆盖完整。

  • Cxty(Cyclomatic Complexity): 圈复杂度, Jacoco 会为每一个非抽象方法计算圈复杂度,并为类、包以及组(groups)计算复杂度。圈复杂度简单地说就是为了覆盖所有路径,所需要执行单元测试的数量,圈复杂度大说明程序代码可能质量低且难于测试和维护。

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

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

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

报告解析参考 https://www.jianshu.com/p/ef987f1b6f2f

增量代码覆盖

  有时候开发测试团队并没有遵循那么标准的流程,我先上一版,都改完后再上一版,这就是我们经常所说的全量覆盖率对于日常迭代,尤其是敏捷开发模型下面其实是没有什么价值的。

  什么意思呢,张三可能在下午4点的时候测了一版,他的覆盖率是80%,这时候李四是一个开发,就改了一点代码,但张三没有必要把所有代码都改一遍了,他只测李四的修改就可以了,这个时候他的代码覆盖率可能从第一个全量版本的80%一下子跌到下一个版本可能只有10%。

  所以说我们在很多实际情况下要统计增量代码的覆盖情况,这个就需要我们自己来写脚本,来做代码库的diff,来对比两个代码库diff的不同点,来看是否被统计,我们要二次渲染、修改这个报告。

Jacoco增量覆盖二次开发https://blog.csdn.net/tushuping/category_10738357.html

https://www.freesion.com/article/52161532714/

1、计算出两个版本的差异代码(基于git)

2、将差异代码在jacoco的report阶段传给jacoco

3、修改jacoco源码,生成报告时判断代码是否是增量代码,只有增量代码才去生成报告。

缺点

jacoco缺点 https://www.sohu.com/a/498359940_120941200

jacoco二次开发后,可以做增量代码的报告,仍有待解决问题:

1、怎么解决已经测试过的代码的报告。如:100条case,测了50条后,开发修改代码重新部署,已经测过的50条case的报告怎么获取?

2、测试环境部署的分支有时候是test,有时候是feature,怎么判断增量代码是不是你当前项目的,测试环境部署需要有明确分支规定