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