前言
01
背景
但是覆盖率有一个致命的缺陷就是如果功能没有开发,你永远都不可能测试到对应的代码覆盖,当然这是一个不太现实的话题,需要开发测试对需求必须熟悉的前提下开展,覆盖率测试对测试辅助作用还是比较大的。
02
平台参考意义
通过代码覆盖率报告可以明确,哪些代码是执行过的,哪些代码是没有执行过的。对于那些没有执行过的代码,测试人员就需要思考是否是代码逻辑设计问题还是测试用例问题。如果是代码逻辑问题,那么测试人员需要与开发人员和产品人员沟通,达成需求理解的一致性。如果是测试用例问题,那么需要补足缺失的测试用力场景,尽可能保证所有重要的场景都覆盖到了,避免未测试的代码上线造成应用服务故障。
开发人员通过覆盖率报告可以获得代码的执行流程走向,帮助开发梳理理解代码,起到codereview的作用。同时通过分析生产环境的代码覆盖率报告,还可以区分出真实的用户请求执行覆盖情况,区分“有用/无用”代码,有利于优化简化代码逻辑,从而起到减少不必要的无用代码。
代码覆盖率是一个确切的数值,能够从一定意义上通过量化测试代码数据来基本衡量测试的充分性,将模糊定义的代码质量用精准的数据来量化,是白盒测试和代码质量的衡量指标。从代码覆盖率报告可以明确,哪些代码是执行过的,哪些代码是没有执行过的,从未执行过的代码可以推算出测试用例是否充足,帮助测试人员理清代码逻辑和完善测试用例。代码测试覆盖率作为一项衡量代码质量的重要标准,可以在测试阶段发现隐藏问题,将隐性问题消灭在上线前期,减少线上问题发生。
03
平台整体方案设计
系统集成全量代码覆盖率统计和增量代码覆盖率统计(可以基于Git commit id或者分支名称对比差异),基于Jacoco的On-The-Fly在线模式,通过系统定时统计覆盖率,或者QA手动触发统计操作。
基本流程如下:
平台设置基准分支和对照分支,同时触发代码覆盖率统计,通过对.exec文件分析,以及增量代码差异分析,获取全量和增量覆盖率统计报告。
04
重点模块
我们的设计方案也是基于jacoco和superJacoco做相应的二次改造开发(感谢前辈们的努力),生成我们所需要的覆盖率模型,并通过jacoco开放的API实现相关功能。
Code Coverage配置 录入需要统计的维度信息,包括:程序源代码Gitlab地址、统计的模块名称、基准和对比的版本、部署的服务器IP、java版本、选择统计的类型(全量或者增量)等;
核心计算程序扫描处于初始化状态的数据,执行数据计算操作;
克隆代码:根据配置的git地址克隆代码到本地(使用eclipse的JGit);
解析模块:解析源代码中pom.xml配置文件获取有效的模块(modules)列表,持久化入库;
编译代码:执行命令cd到源代码目录下,执行mvn clean compile编译代码并输入日志到log文件中;
解析差异代码并计算增量方法:基于JGit分析计算基准版本和对比版本之间的差异的文件(排除删除和单元测试的文件),筛选出所有以.java结尾的文件。如果文件为新增(ADD)则添加整个文件到增量方法;如果文件为修改(MODIFY),则分别计算基准分支和对比分支代码,将代码方法体计算md5,然后计算出对比分支相较于基准分支有修改的方法名称,持久化到数据库中;
拉取exec文件:遍历所有配置的项目机器地址,通过jacoco dump工具,从项目机器上拉取功能测试的执行轨迹.exec文件,更新到本地;
统计覆盖率报告:通过jacoco-cli工具计算覆盖率,如果增量方法不为空,在统计覆盖率时只统计增量方法,最终生成覆盖率报告。将结果持久化到数据库。
获取到一个全量代码覆盖率的收集,我们把它拆分成了以下几个步骤去实施:获取测试完成后的 .exec 文件(二进制文件,里面有探针的覆盖执行信息, 记录了代码的覆盖情况);
获得本次部署的镜像,拿到插桩后的 classes;
获取基线提交的代码(本次部署的 gitlab 上对应 commit 的代码);
利用 jacoco 的 api 生成报告.
在平时测试过程中,如果是需要看一个应用本次发布的代码和某一次发布的代码之间的差异点是否都已经测试到了,那么我们的关注点就不在于全量的覆盖情况,而是一个增量覆盖,也就是本次的代码和上一次的代码改动量的覆盖情况。我们可以通过改造上述三件套,来实现这样的需求。具体实施步骤如下:
获取测试完成后的 exec 文件(二进制文件,里面有探针的覆盖执行信息, 记录了代码的覆盖情况);
获得本次部署的镜像,拿到插桩后的 classes;
获取基线提交与被测提交之间的差异代码;
对差异代码进行解析,切割为更小的颗粒度,选择方法为最小维度;
改造 JaCoCo ,使它支持仅对差异代码生成覆盖率报告。
基于JGit分析计算基准版本和对比版本之间的差异的文件(排除删除和单元测试的文件),筛选出所有以.java结尾的文件,其中包含了差异代码的具体内容、行号、文件名等等信息。如果文件为新增(ADD)则添加整个文件到增量方法;如果文件为修改(MODIFY),则分别计算基准分支和对比分支代码,将代码方法体计算md5,然后计算出对比分支相较于基准分支有修改的方法名称,持久化到数据库中。
包含核心逻辑操作步骤为:
通过JGit对比文件之间的差异;
分析这些文件之间的差异;
遍历差异文件,并且筛选出所有以.java结尾的文件;
通过基准分支与对比分支的MD5列表,找出他们的差异;
生成统计报告。
通过jacoco-cli工具计算覆盖率,如果增量方法不为空,在统计覆盖率时只统计增量方法,最终生成覆盖率报告。将结果持久化到数据库。
05
效果
核心模块整体操作界面如下图:
增量报告
包视图
类视图
方法视图
代码视图
可视化的html覆盖率报表,协助覆盖情况精准分析,支撑精准测试落地。
序号 | 字段名称 | 名称 | 描述 |
1 | Element | 元素 | 最外层展示分组名称,依次为:包>类>方法 |
2 | Missed Instructions Cov. | 指令覆盖字节码中指令 | 1、Jacoco计算的最小单位就是字节码指令2、指令覆盖率表明了在所有的指令中,哪些被执行过以及哪些没有被执行。 |
3 | Missed Branches Cov. | 分支覆盖率 | 1、对所有的if和switch指令计算分支覆盖率2、用钻石表示 |
4 | Missed Cxty | 圈复杂度 | Jacoco为每个非抽象方法计算圈复杂度,并也会计算每个类,包,组的复杂度。根据McCabe1996的定义,圈复杂度可以理解为覆盖所有的可能情况最少使用的测试用例数。 |
5 | Missed Lines | 行 | 用背景色标识的都算行统计目标,变量定义不算行,else也不算 |
6 | Missed Methods | 方法 | 每一个非抽象方法都至少有一条指令,若一个方法至少被执行了一条指令,就认为它被执行过。 |
7 | Missed Classes | 类 | 每个类中只要有一个方法被执行,这个类就被认定为执行。 |
06
实现成果和实现细节
2、简化&优化Jacoco报告统计指标,使报告指标贴合大众理解,为后续测试提供了一个测试完成后的量化测试报告依据,并且可以反推测试与开发;
3、支持负载均衡的覆盖报告的收集,以及同版本不同时期的测试覆盖率增量测试报告;
4、与传统测试手段对比测试透明度高,评估需求更加清晰,开发代码逻辑的清晰,测试完成的测试后代码测试覆盖率的量化
5、提供全局排除及核心功能覆盖设置功能
全局排除功能:是为了排除项目中的非业务代码或外部包等无用代码,免除计入覆盖率统计与展示.
核心功能覆盖设置功能:则是通过对项目代码进行分析,确定业务的核心功能涉及的类和方法有哪些,录入设置后便可只针对这些核心类方法进行覆盖率统计。
07
未来规划
参考资料
作者简介
汽车之家
马彩利、郑刚、叶微微
智能数据中心效能平台部, 负责金融相关的开发与测试工作。
阅读更多
▼ 关注「之家技术」,获取更多技术干货 ▼