文章来源:本文节选自TesterHome社区爱好者合力编写的《2021接口测试白皮书》。
接口回归测试面临的问题
迭代形态及组织变化
迭代加快
在当今的互联网行业的形态背景下,产品需求迭代都愈加地追求快速交付流动,大部分公司都采用精益产品研发流程,那么在之家经销商bu内部,我们近2年的平均需求交付时间≈4天,而潜在的问题是,由于原自动化接口测试的开发/维护成本较高已追不上迭代,我们大部分的回归测试是被跳过的,更多要依赖开发人员水平+测试人员经验,所以一旦常规的节奏被打破(紧急需求插入、人员状态、人员变化等),回归缺失的问题就会暴露出来,而这种外来变化其实也越来越频繁。
项目/人员变更
随着公司内部布局的各种变化,也会产生一些项目交接/重组,或人员
技术演进
另外随着技术的不断演进,给日常接口回归测试也带来了多元角度的影响:
- 架构或应用内部代码的演进重构的过程本身(容器化、接口合并/
拆分/优化、数据源迁表/迁ES......),需要回归验证支撑。 - 服务节点多,只改了1个服务几行代码,可能需要验证所有调用方。
- 服务链条长,跨BU/服务调用多,通过Mock单服务测试不能独善其
身,集成/联调测试阶段问题多。 - 服务功能类型增多,技术类型需求增多,使用传统回归验证方式效率低,效果差。
解决方案分析
接口自动化vs流量回放
在前述背景下,我们引入了新的流量回放方式来进行接口回归来解决上述问题,使用上一个版本的响应情况作为基准,默认是正确的,然后与新版本的情况做对比,检查差异项是否为预期内。相比原有的接口自动化测试,这种测试方式的收益比要更高。整体方案
开源方案
当时的流量回放方案大概有 2 种,原理可以简述为:
• 基于 HTTP 接口纯黑盒的回放,如 Diffy。
拉取线上流量请求信息后,在部署了不同版本代码的 2 套测试环境进行 Http
接口的调用回放,实时或离线的比对接口的 response 响应的区别是否符合预期,来进行测试。代表的有 twitter 的 diffy 工具
• 基于代码方法级别的回放及验证,如 Repeater
方案分析
而结合我们实际诉求,以及方案的优缺点,进行匹配:
- 部门内.net 转 java 、接口合并等技术任务,需要这套方案能进行跨语言、平台的回归
- 需要能够兼并读+写接口的回归,即需要 Mock 或数据还原
- 需要能支持集成联调阶段,即对外真实的外部调用
基于分析,我们发现单独基于黑盒的方案,对写接口验证能力较弱(写接口对外只返回一个状态 ok),测试数据不稳定 case 类型偏移(如线上有数据的商家,在测试环境回放结果空)。而单独基于 Repeater 的方案,又只能支持单语言平台(不能支持.Net 转 java 的实际诉求),Mock 功能不能兼容有 IO 变化的迭代(只能匹配到已记录的 db 查询) 。
没有完美解决所有问题的银弹
经过综合考虑,最终确定:
• 对于读接口(80%):自研 AutoDiff 进行黑盒流量回放测试。
• 对于写接口:使用 Reapeater 进行回放测试。而其他仍不能支持的写接口场景(如有 io 变更、跨语言等情景)再进行常规的接口自动化测试或手工测试。
底层写接口方案,是基于 Repeater 进行二开和包装,录制数据上报 kafka
核心问题解决
写接口(Repeater)回放原理
Repeater 是阿里基于 JVM SANDBOX 开发的一套开源框架,核心功能是提供了通用录制回放能力,其官方介绍:
无侵入式录制 HTTP/Java/Dubbo 入参/返回值录制能力(业务系统无感知) ;
基于 TTL 提供多线程子调用追踪,完整追踪一次请求的调用路径 ;
入口请求(HTTP/Dubbo/Java)流量回放、子调用(Java/Dubbo)返回值 Mock 能力。
也就是说 Repeater 基于 sandbox 提供的能力,可以记录到 Java 方法级调用的入参、返回值、调用顺序等信息,并把外部调用的入口请求,定义为入口调用(比如 http 接口默认将 Httpservlet 的拦截作为入口调用),而将该次请求所产生的下游调用,称为子调用,流量的录制过程就是把入口调用和子调用绑定成一次完整的记录。
具体哪些下游方法调用会被记录下来,可以由埋点插件自定义,应用于业务时,一般会将 Dao 外部 IO 层调用的 Java 方法进行埋点,拦截录制,比如数据库查询或外部接口调用的返回结果实体。录制下流量后,在回放时,会重放入口调用,并在运行时从录制的子调用中根据 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 内包含的时间戳等字段不一致导致的误报,
除此外,我们还不同问题进行针对优化,如下移原 mybatis 插件的 insert
埋点,用于解决 response 内回显插入数据主键 id 场景下,回放值为 null 的误报问题等。
而目前还存在的误报,主要是会因新老版本代码逻辑变化,子查询无法匹配
用例计算
那么在提高用例质量方面,我们设置了一套组合的测试用例生成规则,生成
时可以按照以下几个规则,分别提取指定数量的 case,合并成一份 case,兼顾时效、与覆盖等维度,相互弥补:
**A.近期日志测试(保证时效+随机性) **
**B.人工录入(支持重要/打底场景) **
直接添加录入,或导入 txt,用于创建部分打底的 case
**C.精准提取(高覆盖低冗余) **
提取日志时,按照一定规则对同一接口的流量,进行分类,根据配置的每个接口所需条数,尽量返回更多不同类的。分类依据有如下:
1)url 参数
根据 url 的参数组合及值进行处理,清洗掉无关的参数等后,进行类似如图的分组后平均提取,后续会继续补充一些针对参数值的定制化规则。
2)response
根据历史录制/回放过的流量 response,按照是否 json,$.result 节点的类型进行一层大类的分类,然后不同大类选择不同的特征项,对流量进行标注,并具体计算出具体的特征坐标值。
在生成流量时,每大类都按照 Kmean 算法聚类,对于异常的大类,类簇 k 最多 2 类,然后每个聚类平均提取。
3)方法调用链覆盖情况
计划可以结合 jacoco,对流量覆盖的方法进行记录,并将覆盖路径相同的 case 标识为同类的 case。目前还在计划中。
**D.参数化生成(满足部分单接口专项) **
录入某条种子请求,将需要参数化的值以{series}&withEqid=true&${cidAnddid}
POST--
并分别录入每个参数(如 series、cidAnddid)的数据文件,执行时将按照各参数值,生成笛卡尔积的 case 集合。
用以支撑线上数据量覆盖不全或希望全量测试,需根据 db 内现有数据构造生成 case 等情况。
跨平台、跨接口比对
在前述提到如.net 迁 java 项目接口、接口优化等的各类需要做回归验证的技术任务中,常常伴随着新老版本的接口不能直接比对的情况,比如下图的 2 个新旧版本 json 响应:由于 2 套新旧代码模板定义的接口 protocol 不同,导致结果内容存在了不
同节点。如果直接比对一定是会报错,所以我们在对比时进行了提取配置,分别将.result.list 进行提取,然后再进行比对就满足了这些有变化的场景。
**跨接口场景(接口合并/拆分/优化等类型的变更) **
那么如果上面的场景支持了,其实完全不同的 2 个接口,也是可以通过这个思路,可以将相同业务逻辑的字段,进行自定义映射关系的配置,进行提取,将
2 个响应都转化为相同的结构,然后即可进行后续的比对以及可视化的结果展示。
比如我们有一个活动车型列表接口 A,与另一个业务线的接口 B,返回的车
转化后的 json 结构是相同的,可以清晰的从 2 个完全不同的接口中,只提
取展示看到我们关心的差异,然后将 2 份新的样本(具有相同结构)进行比对,相比针对每个需求单独编写接口自动化测试代码更为直接简单。
闭环功能
除了上面一些核心问题的解决,也需要提供配套的闭环辅助功能,以及各功能的易用优化,使工具达到更加快速、高覆盖的目标。
比如写接口的报告可以清晰的切换查看 response,以及子调用的详情。读接口的报告将所有 case 集成在一个报告中,可视化的 diff 信息、便捷的设置入口、支持持键盘↑↓键切换查看、切换查看原始/处理后的 response。。。目前下一步计划还可以从报错聚合分析和展示方面,进行一些优化。
效果评估方式
对于整个方案的效果评估,我们从 3 个角度进行了分析,基于这些指标进行定时的观察,以便于后续的计划与跟踪:
业务价值
实际落地后,为部门解决的问题,或者创造的业务价值。对于测试工具,更多的是结合我们当前部门业务特点、技术、以及质保体系的情况,痛点情况来评估引入 Autodiff 流量回放后创建的价值:
- 解决弥补了部门内原来回归测试随机开展的问题。为 90+个后端服务提供需求交付的回归测试保障。
2.为原先因无法全量测试而不敢进行的技术任务,提供了有力支撑。
全年共计支持 15000 +次回归测试,50+次技术专项任务。共计回归约 440 万 条用例 , 在各阶段拦截 100+ 个问题,节省人力约 4 人年。
运营推广情况
工具的落地推广运营部分,我们主要考虑统计了:常用项目接入率(数)、常用接口接入率、项目平均 case 数、已接入项目日常发版执行率、代码覆盖率。
工具本身
在工具本身的能力情况方面,根据我们对这套回归测试工具核心的期许:
能够快速、高覆盖的完成集成回归测试,对相应的观察指标进行拆解。
快速部分的指标可以拆解出:接入时间、执行时间、维护时间(用例拉取/ 维护)、排查时间(千条 case 时间);
高覆盖部分可以拆解为:能覆盖的诉求场景以及测试类型、代码覆盖率高、实际业务场景覆盖率高;
以上过程指标有的可以直接统计,有的不能直接观察统计,可以采取定期访谈的方式,或者替换拆解为其他二级指标,如排查时间更多的用误报率代替。
效果评估方式
基于前述几个方向的指标持续观察,我们下一步会继续优化功能:
- 用例的精准化生成,提高覆盖率,减少维护成本;
- 报错聚合,减少排查时间;
- 尽量能覆盖更多的诉求场景或测试类型;
本文节选自Testerhome社区爱好者合力编写的《2021接口测试白皮书》,点击链接可查看并下载完整版《2021接口测试白皮书》。
想要学习更多关于测试/测试开发技术、测试管理和质量保障的干货知识?
想要结识质量行业大牛和业界精英?
欢迎关注第十届中国互联网测试开发大会·深圳站 >>