背景
随着互联网竞争越来越激烈,产品的变更迭代也越来越频繁,回归测试工作就变得繁重且紧急,这对测试团队的测试质量和效率带来很大的压力。面对越来越多的回归测试任务,以及对回归测试提出的更高的要求,而传统的接口自动化测试的维护成本太高、公司自动化测试薄弱的现状慢慢成为测试团队非常尴尬的存在。团队迫切需要一套可靠、低维护成本的测试方案,目前最火的当属流量回放测试。
目前学而思1对1团队采用的是单服务Mock方案,底层采用的是滴滴的开源方案,团队在开源的基础上结合团队自身的特点,在准(基于代码覆盖挑选流量)、快(分布式执行)、稳(通过引入参照系报告)、易用性(平台化)等多个方面做了进一步的开发工作。
技术方案
整体方案
因为我们需要使用线上的真实流量来进行线下的回放测试,所以我们需要将线上的真实流量保存下来,然后将保存的真实流量在线下环境进行回放一遍。整个核心技术方案就是 流量录制和流量回放。
流量录制过程
即录制线上服务完成一次业务调用,接收到的请求、调用下游服务及其返回、业务最终响应的全部网络信息。
前端或其他应用发送 get 或 post 请求,到达 nginx 服务器,转为 fastcgi 协议,交给 php-fpm 的 worker 进程处理。
代码运行自己的逻辑、根据需要对外发起请求,如 mysql、redis、其他应用的 http 或 rpc 请求并接受这些外部调用的响应。
应用返回业务的响应数据。
以上网络数据,构成了一次完整的业务逻辑涉及的所有网络调用。
整个过程产生的网络数据都会在请求和返回的时候,复制一份发送给recorder。recorder 整合记录后,会将一条完整的请求响应记录保存为一条流量用例(session),存储在 es 中。
流量回放过程
即用线上录制的流量,通过流量匹配 mock 掉下游网络调用请求,完成对线下测试代码进行回放测试。
“system under test”是被测试的服务,“inbond server”用来模拟客户端发起请求,“outbond server”用来模拟 php 业务代码对外调用的其他服务,如 redis。
“inbond server”作为客户端,通过 http,将要回放接口请求信息,发送给被测试的服务,也就是我们的 php 服务。
被测试服务,接收到请求,执行自己的业务逻辑。
当被测试服务需要调用外部接口或存储的时候,发起对外网络调用,此时请求会被转发到 outbound server,通过匹配机制,根据线上记录返回这一条网络请求的响应数据,完成对外调用的 mock。
被测试服务完成业务逻辑,返回结果,与线上响应进行对比。
因为滴滴 RDebug 方案,采用的是传输层录制方案,所以对业务代码零侵入。当前,上线 php 录制项目 66 个,在容器环境下,通过采用旁路 recorder 异步汇总、发送流量信息的方式,对业务的响应时间没有影响,但内存消耗同比增大 2 倍。
流量回放平台(Pandora)
目前滴滴的开源组件已经很好地实现了录制和回放功能。但是,对单条流量的回放,是无法保障项目的整体质量的。基于契约测试的理论,我们的流量回放,是需要覆盖被测试服务线上所有被调用的逻辑,才能保证测试质量。
所以,我们研发了 Pandora,实现了以下几点功能:
1.对于覆盖接口逻辑方面,实现了基于代码覆盖去重的流量筛选。2.对于执行速度方面,采用了 k8s 分布式 Job 并发执行。3.对于回放接口选择方面,通过分析修改函数,仅回放本次修改被影响到的接口。4.对于修改接口和新接口,通过引入测试流量来实现对其的回归。5.对于结果确认方面,采用流量校准反馈的方式形成闭环。6.对于易用性方面,开发了一个 web 平台便于查看报告以及分析。
而要实现基于消费者契约的 API 测试,我们首先要对代码逻辑的充分覆盖,那么如何用更少的流量来保障充分的覆盖呢?
基于代码覆盖去重的流量筛选
在录制阶段,我们会将这个服务接收的所有的请求全部录制。而为了保证测试质量,需要覆盖全部线上逻辑,显然,我们不可能将线上的所有流量全部回放一遍。
为此,我们的研发基于代码覆盖去重的功能。其具体实现如下:
1.我们通过修改 RDebug 源码,添加了流量的代码版本信息及所属的接口。
2.我们通过每天凌晨的定时任务,对前一天这个项目、每个版本(一天多次更新)、每个接口所有的流量进行回放,这里会根据流量产生的代码版本,使用对应的代码进行回放。
3.对于一个项目的一个接口,分析他的代码覆盖度情况,并与相同版本的流量进行对比,如果覆盖的代码完全相同,则执行去重操作。
由此,我们就获得了这个项目,线上被访问所有逻辑的集合。
k8s 分布式 Job 并发回放
经过对测试用例的过滤,我们极大的减少了每个项目,执行流量回放进行回归,需要使用的测试用例数量。但是为了覆盖所有逻辑,仍然需要执行数百甚至上千的流量用例。如果顺序执行,需要耗时数小时,时效性难以接受。为此,我们借助了 K8s 的分布式执行能力,并发执行上百个子任务进行回放,执行时间缩短为之前的百分之一。
改造优化后可以在 6-20 分钟左右,完成一个项目的回归测试。
引入精准回放
在回放的过程中,有一部分误报,是因为其他迭代的修改引入的,那么如何消除这部分误报,使失败的接口都来自于本次代码变更。
对于 php 项目,我们在覆盖度分析的回放中,还会获取这条流量的 trace 分析数据,包括了本次请求,所调用的函数、函数的参数和响应等。保存在数据库中待查。
当开发 push 或 merge 代码到 master 分支,我们会自动分析,本次代码修改,影响了哪些函数,然后,从流量-调用函数库中,获取到本次修改影响的流量,使用这些流量对修改的接口进行精准回放。
同时,我们还收集了回放时对新代码的覆盖情况。
以下图为例,本次新增的 addCommonClue 函数,我们只回放了 add_common_clue 一个接口,在两个版本的 6 个流量,就对本次代码的修改进行了精准的覆盖。
引入测试流量
在我们的早期落地阶段,因为可以使用最宝贵的线上流量,所以一直使用的线上生产流量对新代码进行回归,可以对未修改的接口进行很好的回归覆盖。但如果一个接口修改了,那么此时的线上流量回归失败,其实是一种误报。同时,我们无法覆盖本次迭代新增的接口。
而我们的解决方案是,引入凝结了测试老师智慧的测试流量。
这样,对于一个接口,采用了最新版本的生产流量、最新 3 个版本的测试流量来进行回放。
从结果上分析:
线上和测试都成功:未修改的旧接口
线上失败、测试成功:修改的接口、且测试验证成功、自动化回归成功3
线上失败、测试也失败:修改的接口、测试存在漏测或非预期的代码提交,需要测试进一步判断或补测
无线上流量,测试成功:新增的接口
通过引入测试流量,我们实现了对不同接口类型的回归覆盖,消除了特定的误报。
以本接口回放为例,线上最新版本的流量回放失败,但是下方 feature 的最新测试版本流量回放成功,据此可以判断这是一个修改接口,且测试已经验证,回归阶段无异常,不需要补充测试。
引入结果校准反馈
当我们又准又快的完成了流量回放之后,还需要开发和测试来确认回放结果。当存在失败的回放时,更需要开发判断,是因为这次更新的新功能导致的失败,还是因为更新影响到了不该变动的逻辑,引入 BUG。
目前对于失败的测试,我们提供了 5 种失败类型用于反馈:
BUG: 新的代码,存在 bug
回放错误: 匹配策略,有可能失败,导致回放失败
预期新功能: 新的代码逻辑,导致的预期的回放失败
非预期新功能: 新的代码逻辑,影响了非预期的接口和逻辑
验证缺陷修复: 线上代码是 bug,线下测试代码在进行修复
通过平台化来降低使用门槛
为了方便测试和开发使用,我们还提供了以下功能
提测自动回放
当开发和测试进行提测或合入 master 代码准备上线时,都会触发自动对相关的项目和分支进行回放,约 6-20 分钟后执行完毕,通过钉钉将结果发送到相关群组。
左侧可以查看接口、回放的版本失败情况,右侧展示了失败的简要信息。
点击流量测试的 id,右侧展示测试回放报告和参考回放报告详情。
当我们需要具体的分析失败原因时,可以通过分析对外网络调用的 mock 情况以及日志来查看和分析。
当开发和测试分析完失败原因之后,则需要进行人工校准,对于同一个接口-版本,我们也支持批量校准。
为了实现上述功能,我们的平台主要包括以下三个模块:流量 case 推荐模块、任务执行模块、分析标记模块。
目前 Pandora 覆盖 1v1 所有 php 项目,近 3 个月支持迭代 246 个,日均回放任务 10 个。
为了支持开发和测试老师更好地在测试前,评估对上下游、数据库的影响范围,潘多拉平台提供了影响范围评估功能。
可以检索以下三种情况的影响范围:
1.修改项目内某个函数后,会影响当前项目哪些接口
2.修改当前项目某个接口,有哪些项目的接口会调用当前接口
3.修改当前项目某个 mysql 数据库,有哪些项目的接口,也用到了此数据库
我们通过录制线上流量,收集到这个接口对外的网络调用、数据库调用。通过对线上流量进行回放,收集到这个接口会调用的函数信息。
基于以上数据,当研发修改了某个接口、修改某个数据库的时候,我们可以评估其影响范围,将原本耗时数小时且可能存在遗漏的评估过程,优化为只需几秒钟就可以获取全面、完整的影响范围。
比如在评估或者开发阶段,准备修改某个函数的时候,可以通过检索,确定会影响哪些接口,避免评估的疏漏。
我们可以看到,对于getAfterPoint这个函数
实际上有两个同名函数,属于不同的namespace和类。包括:
对于影响项目的接口,以 tms 为例
点击可以看到具体的接口名称,接口最后的访问时间(默认只统计了近30天有访问的接口数据),录制次数,则表示了这个接口的复杂程度。
当一个项目修改了某个接口,可以通过平台来评估有哪些项目会调用这个接口。这里以 cc-v2 的 rpc 接口 getCidByStudentUid 为例:
通过影响范围评估可以发现,这个接口被 plan 的 getinfo 和 Ims-api项目的 student_info 两个接口调用。测试伙伴就可以有针对性地对这两个接口开展测试工作了。
当开发需要调整 mysql 数据库的表结构,进行更改等操作时,如果希望知道,还有哪些项目,也用到了这个表。可以通过数据库表类型进行检索,当前只支持对表名进行检索。这里以迭代中对 student_follow 表进行修改。查询之后可以看到,一共有 5 个项目,60 个接口会调用这个表。测试伙伴可以有针对性的对这些库表进行测试了。
当前基于流量回放的回归测试,重点是通过线上和线下流量保障未修改接口、修改接口、新接口的修改符合预期,完成自动化的回归测试。未来的努力方向是希望借助 AI 机器学习的能力,自动完成结果分析、用例推荐等功能,赋能业务测试老师,让业务测试老师将更多的时间投入到更有价值的工作中。
[1]
RDebug: https://github.com/didi/rdebug[2]
sharingan: https://github.com/didi/sharingan
· 往期推荐