TesterHome测试之家

AutoDiff流量回放在集成测试中的实践应用-汽车之家

2022-06-28  本文已影响0人  TesterHome

文章来源:本文节选自TesterHome社区爱好者合力编写的《2021接口测试白皮书》。

接口回归测试面临的问题

迭代形态及组织变化

迭代加快

在当今的互联网行业的形态背景下,产品需求迭代都愈加地追求快速交付流动,大部分公司都采用精益产品研发流程,那么在之家经销商bu内部,我们近2年的平均需求交付时间≈4天,而潜在的问题是,由于原自动化接口测试的开发/维护成本较高已追不上迭代,我们大部分的回归测试是被跳过的,更多要依赖开发人员水平+测试人员经验,所以一旦常规的节奏被打破(紧急需求插入、人员状态、人员变化等),回归缺失的问题就会暴露出来,而这种外来变化其实也越来越频繁。

项目/人员变更

随着公司内部布局的各种变化,也会产生一些项目交接/重组,或人员

转组的情况,一般在业务上会有一波新的改版,而后端接口技术上也会进行重构。但由于刚刚交接,产品、测试、开发都不会非常熟悉原有系统,传统回归测试手段,由于无法确定预期很难进行。

技术演进

另外随着技术的不断演进,给日常接口回归测试也带来了多元角度的影响:

解决方案分析

接口自动化vs流量回放

在前述背景下,我们引入了新的流量回放方式来进行接口回归来解决上述问题,使用上一个版本的响应情况作为基准,默认是正确的,然后与新版本的情况做对比,检查差异项是否为预期内。相比原有的接口自动化测试,这种测试方式的收益比要更高。

整体方案

开源方案
当时的流量回放方案大概有 2 种,原理可以简述为:
• 基于 HTTP 接口纯黑盒的回放,如 Diffy。
拉取线上流量请求信息后,在部署了不同版本代码的 2 套测试环境进行 Http
接口的调用回放,实时或离线的比对接口的 response 响应的区别是否符合预期,来进行测试。代表的有 twitter 的 diffy 工具

• 基于代码方法级别的回放及验证,如 Repeater

回放思路相同,但基于代码方法级别的录制和回放验证,一般将查询数据库等操作,在录制时进行记录,回放时进行 mock,从而增加测试数据的稳定。Java 中代表的有阿里的 Repeater  

方案分析
而结合我们实际诉求,以及方案的优缺点,进行匹配:

  1. 部门内.net 转 java 、接口合并等技术任务,需要这套方案能进行跨语言、平台的回归
  2. 需要能够兼并读+写接口的回归,即需要 Mock 或数据还原
  3. 需要能支持集成联调阶段,即对外真实的外部调用

基于分析,我们发现单独基于黑盒的方案,对写接口验证能力较弱(写接口对外只返回一个状态 ok),测试数据不稳定 case 类型偏移(如线上有数据的商家,在测试环境回放结果空)。而单独基于 Repeater 的方案,又只能支持单语言平台(不能支持.Net 转 java 的实际诉求),Mock 功能不能兼容有 IO 变化的迭代(只能匹配到已记录的 db 查询) 。

没有完美解决所有问题的银弹

经过综合考虑,最终确定:
• 对于读接口(80%):自研 AutoDiff 进行黑盒流量回放测试。
• 对于写接口:使用 Reapeater 进行回放测试。而其他仍不能支持的写接口场景(如有 io 变更、跨语言等情景)再进行常规的接口自动化测试或手工测试。

整体方案: 在应用层,可以支持各开发阶段及各种回归需求场景,而在底层,读写接口分别由 2 套基于不同实现的方案进行支撑,同时按照流量的子调用情况,进行读写接口方案的适配: 底层的读接口方案,整体架构如下,数据层支持从多种数据源收集并整形流量,经过一定的清洗、筛选、解析,精准规则解析并进行用例计算。测试过程采用实时比对方式,比对算法支持不同接口、进行噪音处理等处理,最终汇总生成报告及结果分析。

底层写接口方案,是基于 Repeater 进行二开和包装,录制数据上报 kafka

经 flink 消费后,加密后存储到 hbase,并提前进行分词后,再存储到 ES 索引用来检索。录制支持组件进行了落地适配,丰富了部门内常用组件的若干插件。并提供了应用层的环境、配置管理等:

核心问题解决

写接口(Repeater)回放原理

Repeater 是阿里基于 JVM SANDBOX 开发的一套开源框架,核心功能是提供了通用录制回放能力,其官方介绍:

无侵入式录制 HTTP/Java/Dubbo 入参/返回值录制能力(业务系统无感知) ;
基于 TTL 提供多线程子调用追踪,完整追踪一次请求的调用路径 ;
入口请求(HTTP/Dubbo/Java)流量回放、子调用(Java/Dubbo)返回值 Mock 能力。

也就是说 Repeater 基于 sandbox 提供的能力,可以记录到 Java 方法级调用的入参、返回值、调用顺序等信息,并把外部调用的入口请求,定义为入口调用(比如 http 接口默认将 Httpservlet 的拦截作为入口调用),而将该次请求所产生的下游调用,称为子调用,流量的录制过程就是把入口调用和子调用绑定成一次完整的记录。

具体哪些下游方法调用会被记录下来,可以由埋点插件自定义,应用于业务时,一般会将 Dao 外部 IO 层调用的 Java 方法进行埋点,拦截录制,比如数据库查询或外部接口调用的返回结果实体。 像使用 retrofit 的接口调用,录制后的内容是这样的:

录制下流量后,在回放时,会重放入口调用,并在运行时从录制的子调用中根据 MockStrategy 搜索匹配的子调用,如果有调用到已录制的子调用,则进行 throwReturnImmediately 返回录制时记录的返回值,没有匹配结果则抛出异常阻断流程。

而针对需要录制的下游方法调用类型,官方已提供了若干常用的插件,在落地过程中针对我们部门内部常用的组件,补充了若干插件的开发,截至 21 年中旬支持的插件大概有:

降噪处理

在流量回放测试(可以说是所有接口自动化测试)落地中,误报率一直是生命线一般的指标值,同时影响着稳定性、结果可信度、排查时间等,是至关重要的一项指标。

读接口-智能去噪
对于黑盒的读接口方案,误报最大的因素是噪音,在实际测试中,大部分的接口 response 都会包含一些时间戳或者广告 pvid 等字段,每次请求生成的都不一样,导致比对不一致报错,但其实业务上并没有问题,有的项目甚至所有 100% 的 case 都会因此报错。比如下面这几种:

可以看到噪音字段的种类、字段名多种多样,不同项目光时间戳的字段就有 cdntime、timespan、timestamp 等,只提供人工配置忽略的话,非常繁琐耗时不可接受。

针对噪音字段的特性是,这些字段的变更并非本次变更所导致,也就是说即使本次代码没有变更,同样的环境,多次刷新下,这个字段值也会变。

由此我们将黑盒回放的过程进行了优化,由原来的新旧代码各请求 1 次,优化为会对旧代码环境请求 2 次,将差异的字段记录下来,就可以认为是我们想要忽略的噪音,在和新代码环境响应进行比对时过滤掉,剩余的差异才是真实代码差异所导致的变更。同时也支持人工配置的指定字段。

为了进一步优化这个机制的效果,减少出现 “请求 2 次旧代码的时候这个字段没变,但请求新代码的时候正好变了,比如秒级时间戳” 的偶发事件,请求顺序需要固定为: 请求旧代码-->请求新代码-->请求旧代码。

这样,如果 2 次请求旧代码时间戳变了那它会被识别为噪音没问题;而如果 2 次请求旧代码时间戳没变,那么时间是流逝的,夹在中间请求的新代码时间戳必然也是一致的,这样该字段就不在差异范围。

而对于对相同代码环境请求 2 次时,哪些类型的 response 差异可以被标识为噪音,主要考虑了以下几种:
• json 节点 value 不同:是噪音。此类差异被识别为噪音的差异项,将记录这些节点 jsonpath,并在新、老代码报文比对时进行忽略。
• json 节点 key 不同:不视为噪音,虽然我们的大前提是连续 2 次请
求线上代码环境,但可有可能因环境或第三方问题造成某次请求报文结构不同,我们的目的是识别噪音用于后期忽略,为了防止误加,此类差异,不会视为噪音。
• json 数组个数不同:不视为噪音,理由同上,因为不是叶子节点,
而是数组某个元素的话有可能比较大的节点,直接作为噪音去忽略的话,很有可能造成最终漏报。但仍有一些情况是推荐类接口,有可能第一次推荐 3 条内容、第二次推荐 4 条内容,这种情况下数组个数在业务上是应该被视为噪音的,但目前没有对此进行特殊识别。
• json 数组顺序不同:是噪音,主要出现于部分推荐接口,或不关心列表排序的接口。
• http 层响应码、响应头不同:是噪音(由于大部分项目功能测试不
关心头信息,所以 AutoDiff 中,默认不比对头,只有配置了要比对头的项目,才会提供这部分的检查及去噪)

读接口方案引入这套去噪机制后,效果显著,如果是带有时间戳的项目,报错率可以由>90%降到<10%。

而仍存在的误报,主要是推荐类业务的 List 接口,目前无法解决,即接口业务上每次会推荐数据不同的数据,这种类型的接口可以设置根据 schema 比对,但为了不漏报,故没有进行固定忽略。

写接口-完善 Mock
对于基于 Repeater 的写接口方案,由于其自带的 Mock 支持,出现噪音的
场景与原因与黑盒并不相同。比如上面提到的推荐数据变更导致的误报,在
Repeater 中开启 Mock 后并不存在(查询时会返回录制的数据,并不会真实重复查询)。

但 Repeater 方案中,因 response 内包含的时间戳等字段不一致导致的误报,

也同样存在,主要是由于这些字段是由 Date、随机数、native 原生方法等生成的,这些方法 jvmsandbox 并不能进行拦截录制,回放时还是真实调用了。 解决方式是,将这些无法拦截的 native 方法用可以拦截的 java 代码封装一层方法,在项目中引入,并在编译阶段,将代码中包含这些方法的部分替换为引用我们自己定义的方法,这样构造方法、原生方法就变成了普通的 java 方法。然后提供插件对这些普通 java 方法感知和干预,即可以成功的录制到时间值,用于后续 mock:

除此外,我们还不同问题进行针对优化,如下移原 mybatis 插件的 insert
埋点,用于解决 response 内回显插入数据主键 id 场景下,回放值为 null 的误报问题等。

而目前还存在的误报,主要是会因新老版本代码逻辑变化,子查询无法匹配

到,导致的误报,比如原来查 2 次库,现在只查 1 次,或者直接改成查 solr 了等。以及因部分查询类型未完全埋点,未被录制下来导致 case 失败误报的情况等。

用例计算

那么在提高用例质量方面,我们设置了一套组合的测试用例生成规则,生成
时可以按照以下几个规则,分别提取指定数量的 case,合并成一份 case,兼顾时效、与覆盖等维度,相互弥补:

**A.近期日志测试(保证时效+随机性) **

接入各个来源记录的流量,拉取最近期的随机 case。可以进行人工指定配置,在业务上支持使用者的不同测试需求,对整个站点所有接口,各测试部分 case,或是对某个单接口进行的专项测试,可以灵活设置。

**B.人工录入(支持重要/打底场景) **
直接添加录入,或导入 txt,用于创建部分打底的 case

**C.精准提取(高覆盖低冗余) **
提取日志时,按照一定规则对同一接口的流量,进行分类,根据配置的每个接口所需条数,尽量返回更多不同类的。分类依据有如下:
1)url 参数
根据 url 的参数组合及值进行处理,清洗掉无关的参数等后,进行类似如图的分组后平均提取,后续会继续补充一些针对参数值的定制化规则。

2)response
根据历史录制/回放过的流量 response,按照是否 json,$.result 节点的类型进行一层大类的分类,然后不同大类选择不同的特征项,对流量进行标注,并具体计算出具体的特征坐标值。
在生成流量时,每大类都按照 Kmean 算法聚类,对于异常的大类,类簇 k 最多 2 类,然后每个聚类平均提取。

3)方法调用链覆盖情况
计划可以结合 jacoco,对流量覆盖的方法进行记录,并将覆盖路径相同的 case 标识为同类的 case。目前还在计划中。

**D.参数化生成(满足部分单接口专项) **
录入某条种子请求,将需要参数化的值以形式标识,如: GET -- /apiprice/api/XXX?seriesId={series}&withEqid=true&${cidAnddid}
POST--

并分别录入每个参数(如 series、cidAnddid)的数据文件,执行时将按照各参数值,生成笛卡尔积的 case 集合。
用以支撑线上数据量覆盖不全或希望全量测试,需根据 db 内现有数据构造生成 case 等情况。

跨平台、跨接口比对

在前述提到如.net 迁 java 项目接口、接口优化等的各类需要做回归验证的技术任务中,常常伴随着新老版本的接口不能直接比对的情况,比如下图的 2 个新旧版本 json 响应:

由于 2 套新旧代码模板定义的接口 protocol 不同,导致结果内容存在了不
同节点。如果直接比对一定是会报错,所以我们在对比时进行了提取配置,分别将.Data 和.result.list 进行提取,然后再进行比对就满足了这些有变化的场景。

**跨接口场景(接口合并/拆分/优化等类型的变更) **
那么如果上面的场景支持了,其实完全不同的 2 个接口,也是可以通过这个思路,可以将相同业务逻辑的字段,进行自定义映射关系的配置,进行提取,将
2 个响应都转化为相同的结构,然后即可进行后续的比对以及可视化的结果展示。

比如我们有一个活动车型列表接口 A,与另一个业务线的接口 B,返回的车

型应该是同步的,只是附加的其他字段有区别,想要在线上进行定时的比对核验, json 结构如图 可以使用 jsonpath 的映射关系作为 key,各自按路径提取的值作为对应的 value,分别转化组合形成新的 json 结构文本如图。

转化后的 json 结构是相同的,可以清晰的从 2 个完全不同的接口中,只提
取展示看到我们关心的差异,然后将 2 份新的样本(具有相同结构)进行比对,相比针对每个需求单独编写接口自动化测试代码更为直接简单。

闭环功能

除了上面一些核心问题的解决,也需要提供配套的闭环辅助功能,以及各功能的易用优化,使工具达到更加快速、高覆盖的目标。

如与流水线的集成;对报错进行标注,要定期收集,持续解决优化误报的情况;以及分别按照接口、代码维度统计展示覆盖情况,便于对测试以及用例质量情况的一个反馈等。  

而对于报错排查,为了缩短实际落地时错误排查的时间,我们持续基于各方反馈,对报告进行了持续大大小小的优化。

比如写接口的报告可以清晰的切换查看 response,以及子调用的详情。读接口的报告将所有 case 集成在一个报告中,可视化的 diff 信息、便捷的设置入口、支持持键盘↑↓键切换查看、切换查看原始/处理后的 response。。。目前下一步计划还可以从报错聚合分析和展示方面,进行一些优化。




效果评估方式

对于整个方案的效果评估,我们从 3 个角度进行了分析,基于这些指标进行定时的观察,以便于后续的计划与跟踪:

业务价值

实际落地后,为部门解决的问题,或者创造的业务价值。对于测试工具,更多的是结合我们当前部门业务特点、技术、以及质保体系的情况,痛点情况来评估引入 Autodiff 流量回放后创建的价值:

  1. 解决弥补了部门内原来回归测试随机开展的问题。为 90+个后端服务提供需求交付的回归测试保障。
    2.为原先因无法全量测试而不敢进行的技术任务,提供了有力支撑。
    全年共计支持 15000 +次回归测试,50+次技术专项任务。共计回归约 440 万 条用例 , 在各阶段拦截 100+ 个问题,节省人力约 4 人年。

运营推广情况

工具的落地推广运营部分,我们主要考虑统计了:常用项目接入率(数)、常用接口接入率、项目平均 case 数、已接入项目日常发版执行率、代码覆盖率。

工具本身

在工具本身的能力情况方面,根据我们对这套回归测试工具核心的期许:
能够快速、高覆盖的完成集成回归测试,对相应的观察指标进行拆解。

快速部分的指标可以拆解出:接入时间、执行时间、维护时间(用例拉取/ 维护)、排查时间(千条 case 时间);

高覆盖部分可以拆解为:能覆盖的诉求场景以及测试类型、代码覆盖率高、实际业务场景覆盖率高;

以上过程指标有的可以直接统计,有的不能直接观察统计,可以采取定期访谈的方式,或者替换拆解为其他二级指标,如排查时间更多的用误报率代替。

效果评估方式

基于前述几个方向的指标持续观察,我们下一步会继续优化功能:

  1. 用例的精准化生成,提高覆盖率,减少维护成本;
  2. 报错聚合,减少排查时间;
  3. 尽量能覆盖更多的诉求场景或测试类型;

本文节选自Testerhome社区爱好者合力编写的《2021接口测试白皮书》,点击链接可查看并下载完整版《2021接口测试白皮书》

今日份的知识已摄入~
想要学习更多关于测试/测试开发技术、测试管理和质量保障的干货知识?
想要结识质量行业大牛和业界精英?
欢迎关注第十届中国互联网测试开发大会·深圳站 >>
上一篇下一篇

猜你喜欢

热点阅读