作者:chengxiaojun
有赞作为一家 SaaS 公司,除了传统的微商城,还提供了零售、美业等产品解决方案。随着公司业务的快速发展,各业务系统也不断的进行着功能迭代或系统重构,如何保证这个过程中系统功能的正确性和服务的稳定性,是公司测试和开发人员需要面对的一个重要挑战。
目前有赞内部已经有一套机制来保证业务系统的质量,包括一些常规的自动化测试工具和人工测试。但常规的自动化测试工具需要准备大量测试数据,并需要编写各类测试脚本,不但成本高而且效率低。
另外随着业务的快速发展,系统和业务复杂度也在不断提升,需要回归验证的业务场景越来越多。通过编写用例的方式很难覆盖大部分场景,测试人员只能保证对一些核心场景的回归验证,无法实现更进一步把控业务系统质量的要求。
基于以上背景我们研发了有赞自动化服务回归验证平台 - 对比引擎(replay),通过它可以对服务接口进行自动化回归验证,可以大大降低测试成本和提升测试效率。
对比引擎是一个高效的服务回归验证平台,它通过复制线上真实请求到预发环境执行,然后对比线上和预发响应,通过判断线上和预发请求响应结果来识别接口正确性,其中通过请求的响应结果来验证服务接口正确性主要基于这样的经验:
大部分业务迭代过程中对代码的修改最终会体现在接口返回值上,可以通过检测同样请求下接口返回值的差异来验证服务接口正确性
备注:有赞大部分业务系统是线上和预发环境共用一套底层基础服务,包括 DB、ES 和 MQ 等服务
对比引擎通过复制线上真实流量去做自动化回归,很容易发现项目迭代及重构中带来的 bug,它同传统的服务回归验证工具及手段相比有如下优势:
- 对比引擎一期的设计目标是:
支持读接口重放
下面是对比引擎整体架构图
对比引擎主要包含以下几个组件:
客户端 SDK 内部逻辑如下图虚线框所示,通过 AOP切面方式
实现请求(方法级别)的采集,之后将请求异步收集到 MQ 消息队列中
备注:目前只支持读接口的回归验证,写接口不支持主要是防止写接口重放导致出现脏数据,写接口的重放后在后面单独有一节进行介绍
下面是 SDK 的一些设计要点:
下面是对比引擎服务端结构图,主要包含 2 个处理逻辑:
下面是服务端的一些设计要点:
- 对比引擎二期的设计目标是:
支持写接口重放
要支持写接口的重放,首先需要考虑如下几个问题:
- 如何判定服务写接口的正确性?
最简单直观的判定方法是:对于同一个接口,相同的请求参数下响应结果一致则认为是正确。
但是只根据响应结果就能判定接口正确性么?比如有的接口响应为PlainResult<Void>类型,可能内部逻辑是有变更,但是无法反映在响应结果上。
我们可以换个角度来思考,理论上在应用数据上下文一致的情况下,2次同样的写操作产生的数据变更应该是一样的,通过比对数据变更情况就能判断服务写接口的正确性。如果相同则认为逻辑没问题,如果不相同则认为逻辑有问题。这里有2个关键点:
- 需要能感知所有数据变更(变更条件也算,比如变更SQL,相同的变更SQL我们认为产生的数据变更是一样的)
- 需要能构造相同的应用上下文(包括数据等信息)
- 如何保证重放的写请求不会导致业务出现脏数据?
要支持写接口请求重放,必须要保证业务不会出现脏数据,否则会对业务产生不可估量的影响
核心思路:
- 请求录制阶段:记录接口调用过程中所有和外部组件的交互信息(包括请求、响应等关键信息),作为请求的上下文
- 请求重放阶段:mock 接口调用过程中所有和外部组件的交互行为,根据请求参数去匹配请求上下文中的响应信息,mock 请求的响应结果。这里 mock 接口调用过程中所有和外部组件的交互行为,主要是考虑到让业务方去区分外部组件是读或写操作本身就有难度,如果有遗漏就可能会产生大量脏数据,风险太高
其中和第三方外部组件的交互入口需要框架组帮忙配合梳理,好在有赞框架组之前在做调用链路追踪系统时已对所有第三方组件进行过埋点,只需暴露第三方组件交互入口即可实现记录请求参数和响应信息的拦截或 mock
下面是写请求录制阶段流程图重要组件说明
- ReplayClientAspect: Replay Client SDK 的核心组件,负责请求录制,对于写接口需要记录请求上下文信息,对于重放请求不会进行再次录制
- Framework: 第三方框架的核心组件,负责记录写请求调用阶段和所有第三方组件或接口的交互上下文信息(包括请求、响应等关键信息)
流程说明
- ①:接口调用达到 ReplayClientAspect,切面记录请求上下文,并初始化第三方框架交互上下文
- ②:业务逻辑执行过程中和第三方交互时,记录交互请求
- ③:业务逻辑执行过程中和第三方交互完成后,记录交互详情信息,并录入到整个请求的第三方框架交互上下文中
- ④:执行完业务逻辑,记录并保存请求信息(异步方式)
下面是写请求重放阶段流程图重要组件说明
- ReplayClientAspect: 同上,这里需要补充的是对于重放写请求,需要加载请求上下文信息(包括第三方框架交互上下文)
- Framework: 第三方框架的核心组件,如果发现是重放写请求,则负责 mock 写请求调用阶段和所有第三方组件或接口的交互行为
备注:
流程说明
- ①:接口调用达到 ReplayClientAspect,加载第三方框架交互上下文
- ②:业务逻辑执行过程中和第三方交互时,需要从 Replay Client SDK 的第三方框架交互上下文信息中匹配到接口的响应信息
- ③:业务逻辑执行过程中和第三方交互是,mock 交互行为
- ④:执行完业务逻辑,返回响应信息
其中 Replay Client SDK 和 Framework 的交互点有 2 个:
需要注意的是:放了防止框架 bug 或者一些其他不可控的因素对线上系统造成影响,如误操作数据等,方案需要考虑兜底手段。这里想到的是对重放请求加压测请求标,这样即使有问题也只是影响的影子库数据,不会对线上业务数据造成影响
应用只需在接口上添加 @Replay
注解就能实现接入
@Replay
@Override
public ListResult<ItemSkuModel> listBySkuCodes(ListBySkuCodesParam param) {
//这里略去接口实现代码
...
}
通过控制台设置需要采集请求的接口、接口的请求采样 QPS 及重放配置信息
另外通过控制台可以设置对比相关配置,比如忽略比对的字段(有些字段是随着时间变化场景),忽略列表顺序(返回顺序无保证场景)
本文主要介绍了对比引擎的研发背景及实现原理,作为一种自动化服务回归验证工具,对比引擎极大的提升了业务的场景覆盖率和回归验证效率,对于保证线上服务稳定起到了良好的作用,目前公司内部大部分业务(包括商品、库存、营销、会员等)都已接入。
后续一个重点是完善用例库管理功能,包括识别各种请求模板、用例采集功能、用例导出以及跨环境的回归验证(线上引流到测试环境),以更好的完善核心场景测试用例并提升测试效率。
欢迎关注我们的公众号