精准测试是一个 2016 年就提出来的概念,现在越来越多进入到大多数测试工程师的视野内,其目的是为了使得测试过程更加可视化、智能化、精准化。在严选整个质量体系中,精准测试建设也占有一席之地。本文从概念、实践、设计、突破以及未来演进方向几个方面来介绍精准测试结合严选业务是如何设计和落地的。
作为测试同学,一定或多或少的在日常测试工作中碰到过以下“灵魂拷问”:
我写的用例真的有效且全面吗,我的测试真的做到有效覆盖了吗?
回归阶段我到底需要回归什么,回归验证充分吗?
这次的改动到底影响范围有多大?
上下游依赖服务的改动为什么不通知我,引起了无感知线上 bug?
作为一个测试负责人,我怎么直观评估团队里的测试同学的测试覆盖情况?
…
精准测试于 2016 年应运而生,经过很长一段时间的方法论完善,业界对精准测试有了统一认知。
一、什么是精准测试
精准测试的概念
借助一定的技术手段、通过辅助算法对传统软件测试过程进行可视化、分析及优化的过程,使得测试过程更加可视化、智能、可信和精准。
精准测试的目标
精准测试的核心思想就是使用非常精确和智能的软件来解决传统软件测试过程中存在的问题,从根本上引领从经验型方法向技术型方法的转型。质量的评估不再完全靠个人经验和业务熟悉度,而是通过精准的数据来判定。在测试资源有限的前提下,将用例精简到更加有针对性,提高测试效率,有效的减少漏测风险。
精准测试的核心:双向追溯
正向追溯:开发人员可以看到测试人员执行用例的代码细节,以方便进行缺陷的修复,测试数据可以直接为开发调试提供依据,快速定位并修复缺陷。
逆向追溯:测试人员通过修改的源代码快速确定测试用例的范围,极大减少回归测试的盲目性和工作量,快速修订测试用例,达到测试覆盖率最大化。
在经过很长一段时间的方法论完善,不少互联网公司包括阿里、蚂蚁、腾讯、百度、有赞等均对精准测试进行了落地实操。我们也针对严选自身的情况和现状做了一些技术调研和可行性分析,开启了精准测试在严选的实践。
二、精准测试在严选的实践
严选的精准测试将贯穿需求生命周期,围绕“我要测什么”和“测的怎么样”两个关键字展开,支持手工测试场景和自动化测试场景,实现数字化的多维测试辅助。
提测阶段
版本提测后,通过手工执行触发【代码分析】获得本次提交版本中精确到接口维度的变更影响范围,为测试同学制定测试方案和测试范围方向上提供参考。同时针对变更接口去我们的自动化用例库筛选和匹配用例,解决我要测什么。
测试阶段
测试完成后通过手工执行【覆盖率分析】获取本次测试版本变更部分的测试覆盖情况,测试同学可以根据代码染色结果来分析本次测试的完成度并进行查漏补缺,同时驱动测试同学加强代码的阅读和分析,来提升测试深度,解决测得怎么样。
回归阶段
准入回归环境时,触发严选自研的质量平台进行测试用例集回归执行,除了测试用例的执行结果外,还有针对本次执行集的增量/全量代码的覆盖率结果分析。
发布阶段
在发布流程中,通过 CI 也会触发触发覆盖率卡点检测,如果未达到预期配置的卡点阈值,代表着存在质量风险,将会阻塞发布流程。
三、平台的设计架构
整体架构底层依赖一些开源的工具(如 Jacoco、Jgit、ASM、maven 等),上面结合业务特点做了一层封装,内部则采用工厂模式的设计模式来实现每个核心组件的独立使用和流程编排。最上层则对外提供一系列 Api,给质量平台以及其他调用方来使用。
代码处理
拉工程代码:主要用于后续切换分支、编译、代码 Diff 计算、工程 ASM 扫描和代码染色等操作。
代码 Diff 计算:主要是基于开源的 Jgit 和 JavaParser 来做代码 Diff 计算以及 AST 语法树分析,分析出变更的 class 以及内部相关信息。
代码编译:会在当前版本的代码上做 mavn/Gradle 编译,主要用于下一步 ASM 代码扫描。
代码静态分析
对于字节码扫描,目前一些常用的字节码操纵框架,ASM/Javassist/bcel 都可以实现(ASM 整体性能更优;Javassist 提供 API 更加丰富,可读性和上手更容易)。
生成全局方法调用关系:在扫描分析中需要剔除掉一些不关注的方法,例如类的加载方法(、等)以及非本工程下的依赖包。ASM 这边的话可以通过解析字节码解析结果对象中的 instructions 属性中的 AbstractInsnNode 来筛选出方法的 MethodInsnNode,然后通过链表的解析处理最终可以形成所需要的调用链关系。
递归寻找方法的调用关系:通过 Diff 出来的 class 中的方法信息在调用链中关系递归来找到最顶层的调用方法。
接口和实现类的桥接:如果一个方法的上一层调用是接口的实现类,那么分析链路就会断掉,此时就需要通过接口实现类所对应的 interface 类来做桥接。
内部类桥接:匿名内部类编译过程中会生成一个类似 A$1 的 class 文件,根据字节码文件中的 EnclosingMethod 字段可以判定上层调用方的类名和方法名,从而可以完成方法和匿名内部类方法的桥接
结果处理:实际结果分析出来可能会存在大量业务上不关注的方法(比如已经废弃没有被调用的方法、实体类对象中的 get/set 方法,一些拦截器、日志、bean 的 config 注册等等),因为剔除的范围过大而且不可控,目前我们采用的方式是通过正向白名单配置类注解+方法注解的方式来筛选需要关注的 class(可动态配置扩展)。
上下游依赖分析
通过严选的服务依赖平台,能够获取到变更接口上下游服务和接口的影响。
用例智能推荐和执行
根据以上分析出的每个变更的单元,会从我们的自动化测试用例库来筛选和匹配合适的自动化用例推荐给测试人员。
测试人员可以在可视化面板上看到每个变更单元的信息、依赖信息、用例推荐信息,如下图:
四、我们的突破
原生 Jacoco 不支持分支或版本之间的覆盖率计算,如何解决?
我们设计的方案是基于 JaCoCo 做相应改造,生成我们所需要的覆盖率模型,并通过 JaCoCo 开放的 API 实现相关功能。这里面主要需要解决的点在获取增量代码并解析生成覆盖率上。可以拆分成如下几个步骤:
获取测试完成后的 exec 文件(二进制文件,里面有探针的覆盖执行信息)
获取基线提交与被测提交之间的差异代码
对差异代码进行解析,切割为更小的颗粒度,我们选择方法作为最小维度
改造 JaCoCo ,使它支持仅对差异代码生成覆盖率报告
覆盖率报告只会针对增量部分做展示
对变更代码做出标记,以及展示对应方法的覆盖情况
原生 Jacoco 在服务重新部署后覆盖率数据无法继承,如何解决?
目前我们是通过改造原生 Jacoco agent,在 agent 监听的服务 TCP 连接断掉之后,自身会触发 shutdown,在 shutdown 的钩子里我们加入了触发通知精准平台来获取当前的覆盖率数据,dump 文件到本地,将这个片段的数据和历史老的已存在的片段数据做合并操作(未发生变更的 class 文件可以完成合并)。同时我们设定的是只有在强制清空覆盖率时(用户主动触发清空操作),才会删除本地历史的覆盖率 exec 文件。
ASM 对静态工程代码扫描,是无法支持到一些 Java 多态、AOP 方向的一些调用关系的,如何解决?
目前我们也已经发现了用静态扫描的方式确实无法支持到这个程度的,需要通过做一些动态的处理来分析真实业务的链路走向。目前行业内的一些成熟做法也是通过 Javaagent 的方式对内部方法做一些织入,例如当执行自动化或者功能测试时,可以记录一次请求经过的所有内部方法,这样形成的内部方法调用链将会记录 aop 和多态执行的真正的方法,静态的弱点会得到很大的补充。但是这样就会对性能和织入范围有比较高的要求,这个也是我们目前正在去研究和攻克的方向,整体架构如下图:
我们希望通过线上流量录制回放或自动化回归的方式,来记录每个 traceId 走过的内部方法路径轨迹。然后再进行每个流量的比对分析和去重的方式,最终计算出本次版本的真实测试流量,有哪些路径轨迹已经被测试所覆盖,哪些还未被覆盖,可能会存在质量风险,同时未覆盖的路径是否有用例能够匹配上,最终会将这份数据提供给测试同学做决策。
五、未来演进方向
易用性
目前我们精准平台的基本能力已在严选各业务线逐步开始使用。未来会在应用接入效率、工程的兼容性、执行分析的性能、代码染色的可视化效果、支持移动端以及底层基础能力封装对外提供等方向上去做优化和升级。
精准性
在精准性方面,也是平台最需要保障的事情。我们需要做的事情和攻克的技术难点还很多,比如代码染色更加的精准、支持使用真实流量来动态分析内部的代码走向轨迹、上下游依赖服务影响分析更加合理等等。
智能性
智能主要体现在用例智能推荐、驱动执行和每个被测单元的覆盖度可视化等方面,未来我们的目标是通过精准分析+自动化+覆盖率三大能力的配合形成闭环,有效的精简用例,准确的推荐用例,以提高测试销量和测试质量。
作者简介
陈逸青:网易资深测试开发工程师,专注于测试技术领域的探索和研究。目前主要负责严选测试服务化能力搭建和演进、测试效能提升以及质量架构规划建设等工作。
周倩如:网易严选高级测试工程师,分销线测试负责人。在服务端测试、客户端测试的各项测试技术方面均有涉猎。
评论 4 条评论