滴滴开源Super-jacoco:java代码覆盖率收集平台

3,014 阅读6分钟

Super-Jacoco是基于Jacoco、git二次开发打造的一站式JAVA代码全量/diff覆盖率收集平台,能够低成本、无侵入的收集代码覆盖率数据;Super-Jacoco除了支持JVM运行时间段的覆盖率收集外;还能够和环境无缝对接,收集服务端自定义时间段代码全量/增量覆盖率;并提供可视化的html覆盖率报表,协助覆盖率分析,支撑精准测试落地。

0. 背景

在软件生产交付过程中,我们通过单元测试、接口测试、功能测试等手段来保障软件质量;无论哪种测试手段,case设计是否全面、精简,显得尤为重要。在实际项目测试过程中,case的设计经常会出现以下问题:

1. 开发同学写了大量单测,一直重复执行一段代码逻辑,少数场景或异常代码逻辑并未执行到;

2. 测试同学设计的测试用例经过反复评审,仍然有未覆盖到的异常场景,出现漏测情况;

3. 接口自动化测试case作为日常回归手段,无法确定是否覆盖所有代码逻辑,其可靠性无法评估。

那么,如何才能用最精简的case来保障测试的全面性呢?目前业界比较认可的是手段是通过分析变更代码的覆盖率补充响应的case;我们调研了业界开源的java代码覆盖率统计工具jacoco和EMMA,发现jacoco和EMMA都只支持收集全量代码覆盖率,不能满足精准分析增量代码覆盖程度的诉求。因此,我们亟需一款能够收集变更代码覆盖率的工具。

1. Super-jacoco简介

Super-Jacoco是基于Jacoco、git二次开发打造的一站式JAVA代码全量/diff覆盖率收集平台,能够低成本、无侵入的收集代码覆盖率数据。Super-Jacoco除了支持JVM运行时间段的覆盖率收集外;还能够和环境无缝对接,收集服务端自定义时间段代码全量/增量覆盖率。并提供html格式的可视化覆盖率报表,协助覆盖情况精准分析,支撑精准测试落地。

单测代码全量/增量覆盖率收集:

on-the-fly模式,无需对开发代码做任何改造,即可收集覆盖率数据;

功能测试全量/增量覆盖率收集:

和环境部署平台ebase集成,只需要在JAVA启动命令中添加-javaagent:jacocoagent.jar=includes=com.*即可收集功能测试覆盖率数据;

可视化报告:

可视化的html覆盖率报表,协助覆盖情况精准分析,支撑精准测试落地。

2. Super-jacoco原理

▍2.1. 整体流程

为了支持增量覆盖率收集,我们需要做两件事情:**1)**获取不同版本代码diff文件;**2)**对jacoco进行二次开发,使其支持增量方法列表参数。

▍2.2. 获取增量代码

主要流程:拉取master(参照分支)和feature(提测分支)代码,再通过JGit对两个分支源码进行比对,获取增量代码。以下为部分代码片段:

▍2.3. jacoco 二次改造,支持增量方法列表参数

JaCoCo 对 exec 的解析主要是在 Analyzer 类的 analyzeClass(final byte[] source) 方法。这里面会调用 createAnalyzingVisitor 方法,生成一个用于解析的 ASM 类访问器,继续跟代码,发现对方法级别的探针计算逻辑是在 ClassProbesAdapter 类的 visitMethod 方法里面。所以我们只需要改造 visitMethod 方法,使它只对提取出的每个类的新增或变更方法做解析,非指定类和方法不做处理。改造后的核心代码片段如下:

▍2.4. 执行

只需要在执行的mvn命令中加入-Djacoco.diffFile=变更方法列表,即可收集变更方法的代码覆盖率。如果不传入-Djacoco.diffFile或者Djacoco.diffFile参数为空,则默认收集全量覆盖率。

▍2.5. 报告输出

覆盖率报告如下图,在图中是某个 service 的实现类,在最新的代码中有23个方法,但是只会对变更或新增的5个方法进行覆盖率统计与显示:

3. 特性

  • 通用:既支持单元测试覆盖率收集,也支持手工测试覆盖率收集;既支持全量覆盖率收集,也支持diff覆盖率收集;

  • 无侵入:采用on-the-fly模式,无需对开发代码做任何改造,即可收集覆盖率数据;

  • 高可用:分布式架构,任务机可无限扩展,避免任务机down机或者任务过多时出现性能瓶颈;

  • 可视化:提供html格式的覆盖率报告,可读性高。

4. 架构

5. 如何使用

▍5.1. 数据库安装和初始化

5.1.1 安装mysql数据库,创建数据库后执行sql/db.sql文件中的建表SQL

▍5.2. 编译打包

5.2.2 clone代码,更改application.properties文件中的数据库和gitlab配置:

spring.datasource.url=jdbc:mysql://IP:端口/数据库名?useUnicode=true&characterEncoding=utf8

spring.datasource.username=

pring.datasource.password=

gitlab.username=

gitlab.password=

5.2.3 执行mvn package -Dmaven.test.skip=true生成super-jacoco.jar

▍5.3. 部署

5.3.1 执行“nohup java -jar super-jacoco.jar &”启动代码覆盖率服务,默认端口为8899

▍5.4. 覆盖率收集接口

5.4.1 单测覆盖率接口

1)启动覆盖率收集
URL:/cov/triggerUnitCover
调用方法:POST
参数(body方式传入):{"uuid":"uuid","type":1,"gitUrl":"git@git","subModule":"","baseVersion":"master","nowVersion":"feature","envType":"-Ptest"}
返回:{"code":200,"data":true,"msg":"msg"}
备注:

2)获取覆盖率结果
URL:/cov/getUnitCoverResult
调用方法:GET
参数:uuid(String)
返回:
{"code":200,"data":{"coverStatus":1,"errMsg":"msg","lineCoverage":100.0,"branchCoverage":100.0,"logFile":"file content","reportUrl":"http://"},"msg":"msg"}
备注:

5.4.2 环境覆盖率接口

1)启动覆盖率收集URL:/cov/triggerEnvCov调用方法:POST参数(body方式传入):{"uuid":"uuid","type":1,"gitUrl":"git@git","subModule":"","baseVersion":"master","nowVersion":"feature""address":"127.0.0.1","port":"8088"}返回:{"code":200,"data":true,"msg":"msg"}备注:IP和port为模块部署服务器的IP和端口,在dump jacoco.exec时使用,需要提前把org.jacoco.agent-0.8.5-runtime.jar包拷贝到服务器:/home/xxx/目录,服务启动时需要添加启动参数:-javaagent:/home/xxx/org.jacoco.agent-0.8.5-runtime.jar=includes=*,output=tcpserver,address=*,port=18513 2)获取覆盖率结果URL:/cov/getEnvCoverResult调用方法:GET参数:uuid(String)返回:{"code":200,"data":{"coverStatus":1,"errMsg":"msg","lineCoverage":100.0,"branchCoverage":100.0,"logFile":"file content","reportUrl":"http://"},"msg":"msg"}备注:

6. 总结

在业务快速迭代的背景下,精准测试将是高效测试的发展趋势,代码覆盖率则是其中重要的一环,Super-jacoco将java的代码覆盖率统计做到方便、快捷有助于精准测试的推动和发展,欢迎加入!

7. GitHub项目地址

github.com/didi/super-…

8. 开源团队

团队成员皆来自滴滴车服技术团队

内容编辑 | Teeo

联系我们 | DiDiTech@didiglobal.com

欢迎微信搜索「滴滴技术」关注同名公众号,获取更多技术干货。