接口测试 服务端代码覆盖率统计平台实现 (全量、变更)

匿名 · 2018年11月19日 · 最后由 jacky 回复于 2021年02月20日 · 6664 次阅读

1.背景
覆盖率是度量测试完整性的一个手段,但不建议作为质量目标,可以当做一种发现漏测代码的手段。
基于公司目前的测试现状,打算研发覆盖率统计工具,辅助大家发现日常功能测试、自动化测试过程中漏测的代码,以补充测试场景,防止漏测。
目前主要支持统计服务器端的代码覆盖率,包括全量覆盖率和变更覆盖率。

2.总体设计
使用场景图:

功能列表图:

3.细节设计
覆盖率报告生成流程图:

3.1 Jacoco 介绍
Jacoco 是一个开源的 Java 代码覆盖率工具,Jacoco 可以嵌入到 Ant 、Maven 中,并提供了 EclEmma Eclipse 插件,也可以使用 JavaAgent 技术监控 Java 程序。很多第三方的工具提供了对 Jacoco 的集成,如 sonar、Jenkins 等。关于 JaCoCo 的注入原理以及注入方式,网上有很多资料,这里不过多赘述。在这里我们主要统计远程测试覆盖率,选择的是 On-the-fly 模式。只需在 JVM 中通过 -javaagent 参数指定 jar 文件启动 Instrumentation 的代理程序,代理程序在通过 Class Loader 装载一个 class 前判断是否需要注入 class 文件,将统计代码插入 class ,测试覆盖率分析就可以在 JVM 执行测试的过程中完成。

我们的设计方案是基于 jacoco 做相应改造(这里为什么需要改造 jacoco 呢?因为 jacoco 不支持变更覆盖率报告的生成),生成所需的覆盖率模型,并通过 jacoco 开放的 api 实现相关功能。

jacoco 详细介绍,具体参见:https://www.jacoco.org/jacoco/trunk/doc/mission.html

3.2 全量覆盖率
3.2.1 java agent 部署
JVM 中通过-javaagent 参数指定特定的 jar 文件启动 Instrumentation 的代理程序,代理程序在通过 Class Loader 装载一个 class 前判断是否转换修改 class 文件,将统计代码插入 class,测试覆盖率分析可以在 JVM 执行测试代码的过程中完成。下面举 2 个例子,说明被测应用如何部署 jacoco:

tomcat 方式:
a.修改 tomcat 的 bin 目录下的配置文件 catalina.sh 的 JVM 启动参数如下:
JAVA_OPTS="$JAVA_OPTS -javaagent:[yourPath/] jacocoagent.jar=includes=*,output=tcpserver,address=本机 ip,port=28044"
b.重启 tomcat
c.执行 ps -ef|grep jacocoagent.jar 如果有 jacocoagent 服务说明部署成功

jar 方式:
java -javaagent:[yourPath/] jacocoagent.jar=includes=*,output=tcpserver,address=本机 ip,port=28044 -jar springboot-xxx.jar

部署方式网上很多参考资料,这里重点强调下:现在很多应用都是基于 docker 部署,建议将 jacoco agent 的部署集成到公司内部的部署平台。

-javaagent 参数配置可以参考:https://www.jacoco.org/jacoco/trunk/doc/agent.html

3.3.2 获取 exec 文件(二进制文件,有覆盖率相关信息)
上面的步骤部署 jacoco agent 之后,我们就可以通过 tcp 的方式获取 exec 文件,部分代码片段如下:

也可参见官方 demo:https://www.jacoco.org/jacoco/trunk/doc/examples/java/ExecutionDataClient.java

3.3.3 生成覆盖率报告
生成覆盖率报告的前提是需要有被测服务的源码和编译后的 class 文件,故会先下载被测服务的源码,然后编译,代码片段如下:

编译是直接运行本地 mvn 命令实现的:

有了源码和编译后的 class 文件,则可以生成覆盖率报告,代码片段如下:


也可参看官方 demo:https://www.jacoco.org/jacoco/trunk/doc/examples/java/ReportGenerator.java
至此,全量覆盖率报告生成已完成。因为 jacoco 原生就支持全量覆盖率,所以不需要对 jacoco 进行改造,使用起来也比较简单。

3.3 变更覆盖率
3.3.1 对 jacoco 改造
在讲具体实现步骤之前,先谈下我们对 JaCoCo 做的改造思路。 JaCoCo 的注入逻辑用的是 ASM 库,对于没有接触过字节码注入技术的测试同学来说,改造注入逻辑需要花费较多时间,这里我们用了一个比较快速简单的方式:前面生成全量覆盖率数据的流程不变,只对解析 exec 文件生成报告做改造,生成我们所需要的覆盖率模型。JaCoCo 对 exec 的解析主要是在 Analyzer 类的 analyzeClass(final byte[] source) 方法。这里面会调用 createAnalyzingVisitor 方法,生成一个用于解析的 ASM 类访问器,继续跟代码,发现对方法级别的探针计算逻辑是在 ClassProbesAdapter 类的 visitMethod 方法里面。所以我们只需要改造 visitMethod 方法,使它只对提取出的每个类的新增或变更方法做解析,非指定类和方法不做处理。改造后的核心代码片段如下:

3.3.2 获取差异代码并切割刀方法粒度
这部分会涉及到较多的 Git 操作,我们是用 JGit 实现的。在这一步的主要流程是获取基线提交与被测提交之间的差异代码,然后过滤一些需要排除的文件(比如非 Java 文件、测试文件等等),对剩余文件进行解析,将变更代码解析到方法纬度,如何解析到方法维度?我们使用 JDT 分析源码并解析出每个方法,然后算出新旧文件的方法的 md5 值,最后比较 md5 值是否相同,如果不相同,说明该方法有变更。

获取变更文件的代码片段如下:

4. 效果
工具整体效果如下:

覆盖率报告如下:

欢迎关注个人技术网站:http://www.runtester.com

共收到 15 条回复 时间 点赞

赞赞赞老哥,我的实现方案也差不多,不过更变方法我是根据变更行号找到变更方法的,会排除一些空行、注释行的变更,生成覆盖率的时候我会把 POJO Enum 这些都过滤,这样更专注业务一些~

匿名 #2 · 2018年11月21日
workhard 回复

赞!生成覆盖率的时候过滤哪些类,可以支持配置,让用户自己选择

3.3.2 获取差异代码并切割刀方法粒度 ,这个能详细的说一下嘛?

匿名 #4 · 2018年11月21日
kell 回复

可以加我的公众号:笑笑测试笔记,直接把代码贴给你

加公号并留言了,貌似没看到?

workhard 回复

请教:想问一下,怎么筛选出 POJO 和 ENUM 的?

wozijisun 回复

就是在获取差异文件列表的时候过滤的,过滤依据目前是根据文件名规范来的,比如 VO、BO、PO、Enum 等,不过这几类都做成配置化了

workhard 回复

哦!我遇到的情况复杂了一点儿,主要是不按照规范来。我再想想。

workhard 回复

大牛 能指导下怎么不监测几个某几个类啊

wozijisun 回复

已经找到解决方案了么, 可以指导下么

请问开源了么,有源码么

如果部署的方式是多个虚拟资源,有空闲的就分配给需要部署的应用。那么同一个服务每次 commit 可能部署到不同的空闲资源上,同一个项目的 classes 也会不一样。这样会导致每次获得的覆盖率报告其实只是当前 commit 版本的,不知道楼主有遇到这个问题吗?

workhard 回复

大佬能指导下如何实现的么

bauul [该话题已被删除] 中提及了此贴 08月07日 09:31
bauul [代码覆盖率] 基于 jacoco 的代码覆盖率统计 中提及了此贴 08月07日 09:35

请教一个问题:类里 4 个方法如果有一个修改了,代码重新部署后 整个类覆盖率是 0,有没有解决办法?毕竟这种情况很常见的,怎么处理的?谢谢

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册