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

xiaoxiaotestnote · November 19, 2018 · Last by 大白 replied at May 13, 2019 · 2059 hits

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

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 12 条回复 时间 点赞

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

workhard 回复

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

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

kell 回复

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

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

workhard 回复

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

wozijisun 回复

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

workhard 回复

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

workhard 回复

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

wozijisun 回复

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

请问开源了么,有源码么

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up