BUG越改越多?微信团队用自动化测试化险为夷

BUG越改越多?微信团队用自动化测试化险为夷

腾小云导读

作为后台开发 Coder,你或许会对以下场景感到似曾相识:历史上处理过的 BUG 重复横跳;版别兼容逻辑多,修复一个 BUG 触发了更多 BUG;上线时体系监控毫无反常,过段时间用户投诉某个页面无数据;改动祖传代码时如履薄冰,心智担负极重。为此本文提出一个自动化测验体系,它能够低本钱完结100%的测验用例掩盖率,极大减轻办理自动化测验用例的工作量并进步测验效率,保证后台服务平稳改变。欢迎阅读~

目录

1 布景

1.1 接口自动化测验介绍

1.2 现状及痛点

1.3 为什么要自研

1.4 方针

2 自动化测验体系完结

2.1 全体架构

2.2 一致 HTTP 和 RPC 拜访办法

2.3 接口参数传递(参数池结构)

2.4 JSON Schema 组件

2.5 JSON Path 组件

2.6 改变体系接入与调度

3 用例自动化生成

3.1 现状以及剖析思路

3.2 全体流程

3.3 流量特征剖析

3.4 用例生成

3.5 用例发现与补全

3.6 流量特征运用

4 总结

01、布景

1.1 接口自动化测验介绍

望文生义,接口测验便是对体系或许组件之间的接口进行测验,首要校验数据的交流、传递以及体系间的相互依靠联系等。依据测验金字塔的模型理论,测验分为三层,分别是单元测验(Unit Tests)、服务测验(Service Tests)、UI 测验(UI Tests),而咱们的接口自动化测验便是服务测验层。

单元测验会导致工作量大幅提高,在需求快速迭代和人力严重的布景下,很难继续推进,本文暂不评论。而接口自动化测验简略完结、保护本钱低,且收益更高,有着更高的投入产出比。

1.2 现状及痛点

实际上咱们有一个叫 WeJestAPITest 的自动化测验渠道,它是依据 Facebook 开源的 Jest 测验结构建立的,用于校验后台的接口返回是否契合预期。在这个渠道此前运转了数年的测验,一定程度上保证了后台服务的平稳运转。

但在长期运用中咱们也发现了一些痛点:

遇到失利用例习气性申请跳过测验,自动化测验形同虚设; 版别需求迭代速度飞快,用例落后于需求改变,用例迭代本钱高; 开发同学很难参与到用例保护中,而测验同学对接口逻辑了解不深,编写的用例过于简略、僵硬,导致掩盖率低、用例质量差,开发上线心理担负重。

咱们需求的不只是一个自动化测验体系,而是一个更好用的、办理本钱更低的自动化测验体系。

1.3 为什么要自研

说到接口自动化测验东西,开源有 JMeter、Postman 等,司内也有老练的 WeTest、ITEST 等,这些都是开箱即用的,但经过调研和评价,咱们还是决议自己造一个轮子。考虑的点如下:

测验东西的完结原理并不复杂,完本钱钱不高,保护难度不大;现有东西并不契合事务要求,例如自界说的调度计划,以及支撑内部 RPC 结构;咱们需求把自动化测验与现有的体系连接起来,比方上线体系,用例失利告警体系,流量剖析体系等;当咱们需求一些非标准才能的时候,外部东西很难快速,乃至无法支撑,拓宽性弱;这个体系首要是为了掩盖后台接口测验,运用体会上要更靠近后台同学的运用习气,下降用例办理本钱。

1.4 方针

结合咱们遇到的痛点以及事务需求,自研的自动化测验体系应该具备以下的才能:

它应该是跟完结言语无关的,乃至是无代码的,消除不同编程言语和结构带来的隔阂;编写用例应该是朴实的,用例跟测验服务分离,改变用例不需求改变自动化测验服务;能够支撑场景测验(多个用例组成场景),且能支撑用例间的变量引证;供给多种调度计划,能够按全量调度、按事务模块调度、按用例组调度、按单个用例调度,充分满意事务和调试需求;这个体系要支撑一起办理 HTTP 和 RPC 用例,能够掩盖恳求的上下流链路;尽最大或许下降后台同学编写用例的本钱。

02、自动化测验体系完结

2.1 全体架构

BUG越改越多?微信团队用自动化测试化险为夷

2.2 一致 HTTP 和 RPC 拜访办法

HTTP 和 RPC 恳求在办法上能够被一致起来,其描绘办法如下:

HTTP拜访办法:http://host:port/urlpath + reqbody

RPC拜访办法:rpc://ip:port/method + reqbody

经过这种一致的描绘办法,再结合咱们的事务架构,就能够规划一种通用的拜访办法。后台的体系架构如下图所示:

BUG越改越多?微信团队用自动化测试化险为夷

从 proxy 层往下,一切的调用都是一个个后台服务模块,HTTP 拜访的是逻辑层,RPC 拜访的是服务层。那么只需求装备用例的归属模块,经过模块名 + Client 装备就能够对 HTTP 和 RPC 恳求进行区别以及寻址。

从改变体系的角度来看,咱们的上线改变也是按模块来的。因而把用例归属到一个个详细的模块,是最契合后台同学认知的做法。

因而咱们经过装备模块名这种一致的办法,为运用者供给了一致的办理办法,只需求指定模块名就能够恣意拜访 HTTP 或许 RPC 恳求,其流程如下:

BUG越改越多?微信团队用自动化测试化险为夷

在红色虚线框的流程中,只需求装备模块名,就能够经过模块名获取到 RPC 服务的一切信息,包括其接口界说、恳求包界说、回包界说,这不是一种通用才能,需求事务依据体系架构以及线上环境去拓宽,但这带来了以下便当:

能够支撑恣意的事务 RPC 结构,拓宽性强;只需求装备模块名就能够拜访一切的 RPC 恳求,无需逐一手动上传解析 proto 文件,削减操作进程;不需求关怀 proto 的更新,实时拉取线上 proto 的信息,协议永远是最新版别。

这儿的一致包含两部分:榜首部分是拜访办法的一致(模块),下降了装备用例的本钱;第二部分是数据的一致(JSON),它一致了对回包办法的校验,下降了校验本钱。

2.3 接口参数传递(参数池结构)

很多事务场景的完结都是由多个接口组成的一条链路完结,而且这种链路型的自动化测验,通常会存在参数依靠联系,一个用例的入参,或许要依靠上游呼应回包的某个字段值,因而需求提取出来并传递给下一个接口。如下图:

BUG越改越多?微信团队用自动化测试化险为夷

其解决计划是,经过正则或许 JSON Extracor 等提取的成果作为变量,然后再传递给下流用例运用,这也是很多测验东西运用的办法,可是保护起来不行便利,仍有进一步优化的空间。

所以咱们提出了参数池的概念,将每个用例或许用到的字段都放入一个池子里,这个池子的元素是一个个 key-value。key 是咱们要运用的变量,value 则是 key 对应的取值,值得注意的是,value 既能够是一个字面值,也能够是一个 JSONPointer 的途径,这个途径能够从呼应回包中提取变量值。

在这种办法下,不同用例间的参数依靠不再是从上一个“传递”到下一个,而变成了一个随取随用的池子,因而咱们把它称为参数池。一起咱们经过自界说的语法,完结了一个简略的模板引擎,将咱们引证的变量替换为池子里的 value 值。参数池结构以及运用图示如下:

BUG越改越多?微信团队用自动化测试化险为夷

2.4 JSON Schema 组件

下面贴一段代码看看现有 WeJestAPITest 结构是怎么对返回值做校验的,并剖析一下它或许存在的问题:

function bookInfoBaseCases(bookInfoObject) {
    it('预期 bookInfo.bookId 非空,且为字符串,且等于12345', () => {
        expect(bookInfo.bookId).not.toBeNull();
        expect(typeof bookInfo.bookId).toEqual('string');
        expect(bookInfo.bookId).toEqual('12345');
    });
}

这种校验办法存在以下几个问题:

这是针对单个字段进行校验,假如一个回包里有几十上百个字段,这种手艺办法不或许完结全量字段校验;编写一个用例需求有 js 基础,对其他编程言语的运用者不友好;断语规矩都是一条条散落在代码文件中,展现和办理有难度;调试需求改变测验服务,调试本钱高。

现有结构的不便导致了用例办理上的种种问题,而咱们依据这些不便之处去反向考虑,咱们究竟需求什么样的校验办法,这种状况下咱们找到了 JSON Schema。

JSON Schema 是描绘 JSON 数据格式的东西,Schema 能够理解为模式或许规矩,它能够束缚 JSON 数据应该契合哪些模式、有哪些字段、其值是怎么表现的。JSON Schema 自身用 JSON 编写,且需求遵从 JSON 自身的语法规范。

下面以bookInfo的校验为例,写一份 JSON Schema 的校验规矩:

// bookInfo信息
{
    "bookId":"123456",
    "title":"书名123",
    "author":"作者123",
    "cover":"https://abc.com/cover/123456.jpg",
    "format":"epub",
    "price":100
}
// 对应的JsonSchema校验规矩
{
    "type": "object",
    "required": ["bookId", "title", "author", "cover", "format", "price"],
    "properties": {
    "bookId": {
        "type": "string",
        "const": "123456"
    },
    "title": {
        "type": "string",
        "minLength": 1
    },
    "author": {
        "type": "string",
        "minLength": 1
    },
    "cover": {
        "type": "string",
        "format": "uri"
    },
    "format": {
        "type": "string",
        "enum": ["epub", "txt", "pdf", "mobi"]
    },
    "price": {
        "type": "number",
        "exclusiveMinimum": 0
    }
  }
}

经过比照,JSON Schema 的优点十分清楚明了:

可读性高,其结构跟 JSON 数据完全对应;一切规矩都处在一个 Schema 中,办理和展现清晰易懂;它自身是一个 JSON,关于任何编程言语的运用者都没有额外学习本钱;此外,咱们能够经过一个现有的 JSON 反向生成 JSON Schema,然后在这个 JSON Schema 的基础上进行简略的修正,就能得到终究的校验规矩,极大下降了咱们编辑用例的工作量和时间本钱。

2.5 JSON Path 组件

有了 JSON Schema 之后,咱们校验办法看似现已十分完美了。它既能够低本钱的掩盖全量字段校验,还能够很便利的进行字段类型、数值的校验。

但实际运用中咱们发现有些测验场景是 JSON Schema 掩盖不到的,比方:一条用户谈论有 createtime 和 updatetime 两个字段,需求校验 updatetime >= createtime。这是 JSON Schema 的短板,它能够束缚 JSON 的字段,可是它没办法对两个字段进行比照;一起 JSON Schema 跟 JSON 是1对1的,假如咱们需求比较两个不同 JSON 的同一个字段,它相同力不从心。这就引出了咱们需求的第二个东西 —— JSONPath。

JSONPath 是一个 JSON 的信息抽取东西,能够从 JSON 数据中抽取指定特定的值、对象或许数组,以及进行过滤、排序和聚合等操作。而 JSONPath 只是一个 JSON 字段的提取东西,要利用它来完结一个断语判别还需求进一步封装。

在这儿咱们用一个 JSONPath 表达式来表明一个断语,下面是一些简略的运用示例:

// 校验updateTime > createTime
$.updateTime > $.createTime
// 返回的bookId有必要为某个固定值
$.bookId == ["123456"]
// datas数组不能为空
$.datas.length > [0]
// datas数组中有必要包含某本书,且价格要大于0
$.datas[?(@.bookId=='123456')] > [0]

值得注意的是,JSON Schema 和 JSON Path 断语校验并非二选一,既能够一起校验,也能够依据场景挑选恣意一种校验办法。与此一起,假如项目前后端交互的协议是 XML、 proto 或许其他协议,能够将其一致转为 JSON 格式,JSON 更简略理解且东西链更多更老练,否则咱们将要为每一种序列化的协议都开发一套类似的东西,重复劳动。

2.6 改变体系接入与调度

在这儿,咱们运用异步 MQ 去调度测验使命,它有三个首要的特色:

多触发源 恣意粒度 指定环境
支撑改变体系、办理渠道、例行使命调度等多个来源的使命触发信号。 支撑按全量用例调度、按改变模块调度、按用例组调度、按单用例调度。 支撑调度到现网环境和测验环境,乃至能够指定 IP 对某台机器定向测验。

BUG越改越多?微信团队用自动化测试化险为夷

03、自动化测验体系完结

在具有了一个接口自动化测验渠道之后,咱们面临一个新的问题:怎么快速提高自动化测验的掩盖率?

这个问题有一个隐含的条件,咱们需求一个能够衡量掩盖率的方针,接下来将介绍咱们怎么结构这个方针,并共享一些提高掩盖率的计划。

3.1 改变体系接入与调度

要衡量掩盖率,榜首反响必定是依据前后端约好的协议进行剖析。可是沿着这条思路去剖析咱们遇到了以下几个难点:

协议办理不规范,散落在 git 文档、yapi、wiki 等多处地方,且格式不一致;文档落后于实际接口协议,且可靠性有待考究;协议参数并非都是正交的,运用协议计算出来的参数组合不契合实际状况;因而,运用前后端协议进行剖析这条路是行不通的。因而咱们打算从线上流量入手,对流量的参数特征进行剖析,并运用线上流量来生成自动化测验用例。

3.2 全体流程

BUG越改越多?微信团队用自动化测试化险为夷

3.3 流量特征剖析

一个 HTTP 恳求,咱们通常需求剖析的是以下部分:恳求办法、URL、恳求包、返回包。而结合咱们的事务场景,咱们还需求一些额外的信息:用户 ID、渠道(安卓、IOS、网页等)、客户端版别号等。咱们调研过一些流量收集剖析并生成用例的体系,大多只能对通用信息进行剖析,并不能很好的结合事务场景进行剖析,拓宽性不足。

咱们有一个恳求,其 url 参数为 listType=1&listMode=2、vid 为10000、渠道为 android、版别号为7.2.0,其恳求体如下:

{
  "bookId":"12345",
  "filterType":1,
  "filterTags":["abc","def"],
  "commOptions":{
    "ops1":"testops1",
    "ops2":"testops2"
  }
}

其间 url 和 header 里的参数都很简略解析,不再赘言,下面讲一下 JSON 恳求中的参数提取办法。这儿咱们用 JSONPointer 来表明一个参数的途径,作为这个参数的 key 值,那么能够提取获得以下参数:

// url 和 header 中提取的参数
listType=1
listMode=2
vid=10000
platform=android
appver=7.2.0
// JSON 中提取的参数
/bookId=12345
/filterType=1
/filterTags=["abc", "def"]
/commOptions/opts1=testops1
/commOptions/opts2=testops2

如此一来,参数的表现办法能够一致为 key-value 的办法,咱们调研的东西也基本止步于此,接下来要么是用正交计算用例的办法辅佐人工编辑用例,要么便是对大量流量生成的用例进行去重。

但这达不到咱们预设的方针,咱们无妨更进一步,经过大量的线上流量结构出接口参数的特征,在这儿咱们提出一个界说,接口参数的特征包括五部分:

参数个数;参数类型;参数取值规模;参数可枚举性;参数可组合性。

咱们的工作首要集中在参数的可枚举性剖析,这也是参数剖析的突破点。假设咱们从线上对某个接口进行采样,采样条数为 1W 条,将得到以下的参数:

listType=[1, 2, 3, 4]
listMode=[1, 2]
vid=[10000, 10001, 10002, 10003, ...] // 3000+
platform=[android, ios, web]
appver=[7.2.0, 7.1.0, 7.3.0, ...] // 20
/bookId=[12345, 23456, 34567, 56779, ...] // 4000+
/filterType=[1, 2]
/filterTags=[abc, def, efg]
/commOptions/opts1=[testops1, testops1_]
/commOptions/opts2=[testops2]

有了以上提取到的参数枚举值,咱们设定一个合理的阈值(比方30),就能够判别哪些参数是可枚举的,很明显 vid 和 /bookId 并不是可枚举的参数,在掩盖用例时不需求对这两个参数进行掩盖。

在实践中,咱们发现固定阈值并不能精准识别到有用的枚举参数,阈值需求跟从采样的数据动态调整。不同接口恳求量或许从几十到几十万不等,假如一个接口恳求条数只需30条,每一个参数的枚举值都小于设定的阈值,一切参数都是有用参数,这不契合实际状况。因而阈值要跟着采样条数的改变而改变,能够按恳求数量阶梯改变,也能够按恳求数量成份额改变。关于特定参数,还要供给人工装备快速介入,指定参数是否可枚举。

在咱们知道哪些参数是可枚举的有用参数后,接下来能够对参数的可组合性进行剖析。实际上咱们并不需求剖析恣意两个参数两两是否可组合,依据线上流量去剖析即可。咱们简略给一个例子:

listType=1&listMode=1&platform=android&appver=7.2.0
listType=1&listMode=1&platform=ios&appver=7.2.0
listType=1&listMode=1&platform=web&appver=7.2.0
listType=2&listMode=1&platform=android&appver=7.2.0
listType=2&listMode=1&platform=ios&appver=7.2.0
listType=2&listMode=1&platform=web&appver=7.2.0
listType=3&listMode=2&platform=android&appver=7.2.0
listType=3&listMode=2&platform=ios&appver=7.2.0
listType=3&listMode=2&platform=web&appver=7.2.0

那么在掩盖用例时咱们需求掩盖这9个组合,经过组合剖析咱们乃至能够发现线上是否有错误运用的参数组合,需求是否发生了改变产生了新的组合参数。

要提高掩盖率,本质上便是掩盖一切可枚举参数的枚举类型以及组合,这便是咱们在上面说到过的掩盖率方针。有了这个方针,咱们就能够对掩盖率提出以下计算公式:

大局掩盖率 = 已掩盖的接口数 / 悉数接口数 * 100%
接口有功效例 = 悉数可枚举参数的可枚举值 + 悉数可枚举参数的组合
接口掩盖率 = 已掩盖的有功效例数 / 接口有功效例数 * 100%
PS:当接口掩盖率达100%时视为接口已完结用例掩盖

3.4 用例生成

经过上面对流量的特征剖析以及挑选,咱们得到了一批有用流量,接下来就能够运用这些流量来自动化生成用例,其间最首要的工作是为用例生成校验的 JSON Schema 规矩。其生成进程如下图所示:

BUG越改越多?微信团队用自动化测试化险为夷

如上图所示,任何 JSON Schema 的生成东西所生成的 Schema 都不或许百分百满意事务需求,咱们依然要依据事务场景对 Schema 进行微调。比方在搜索场景下,咱们用一个 results 数组来承载返回成果,生成器生成的 Schema 只约好了 results 字段有必要要存在,而且字段类型为数组类型。假如有一天返回了一个空的 results 数组,那么默认生成的 Schema 是检查不出这个问题的,咱们可认为 results 数组增加 minItems = 1 的规矩,要求 results 数组有必要大于等于 1,下次校验时遇到空数组就能够告警出来。

一起,在用例履行时遇到校验不经过的状况,咱们也规划了一套自动化 promote 用例的流程,不需求手艺对用例进行改动。其流程如下:

BUG越改越多?微信团队用自动化测试化险为夷

其间用例优化分为三种状况:

移除用例:用例已失效,直接删去用例;替换用例:用例不契合预期,从线上依据相同的参数选取恳求重新生成一个用例;优化 Schema:用例中某些字段并非必需字段,或许归于预期内的改变(比方用户的未购变已购导致某些字段被替换)。

BUG越改越多?微信团队用自动化测试化险为夷

咱们运用的 Schema 生成东西是 genson,它可认为一个 JSON 生成对应的 JSON Schema。这个东西有个很重要的特性:它是一个多输入的 JSON Schema 生成东西,能够接纳多个 JSON 或许 Schema 作为输入参数,生成一个契合一切输入要求的 Schema,这一点正是咱们自动化的要害,使得咱们不需求手动编辑校验规矩。下面简略展现一下咱们现在的体系是怎么优化失利用例的:

BUG越改越多?微信团队用自动化测试化险为夷

3.5 用例发现与补全

用例的自动化发现分为两个离线使命:一个是新接口的发现,一个是新用例的发现。

新接口是指咱们有新的功能上线,当线上有流量拜访时,咱们应该及时发现这个新的恳求,并将这个恳求纳入咱们的自动化测验办理规模。

新用例是指经过对流量剖析,发现了新加的可枚举参数,或许之前用例未曾掩盖的参数组合,咱们经过比照线上流量和现已收集落库的用例进行 diff 剖析,得到并生成新的用例。

下图是对用例的自动化发现与补全的简略示例:

BUG越改越多?微信团队用自动化测试化险为夷

3.6 流量特征运用

依据上面说到的流量特征剖析以及用例生成,咱们的用例个数从150+提高到8000+,完结了读接口100%用例掩盖,掩盖率有了一个质的飞跃。

关于写接口完结了掩盖率计算以及用例引荐,极大下降了在编辑用例时的心智担负,不需求自己去结构参数以及遍历一切的参数组合,跟从着引荐的用例去补全即可。

一起针对咱们前面说到的前后端协议涣散在各个地方,且接口与文档不一致的问题,咱们经过线上流量对恳求参数和恳求回包的 Schema 进行继续的迭代,然后再将 Schema 反向生成 JSON, 就能够得到一份最全、最新的接口协议,一起这份协议还能够供给给客户端同学用来结构参数进行 mock 联调。

04、总结

至此,咱们现已完结了整个后台接口自动化测验体系的建立,并完结了预设的悉数方针:

集成 JSON Schema 和 JSONPath 这两个组件,完结了一个无代码以及用例跟测验服务分离的自动化测验体系;经过用例的组合以及参数池结构完结了场景测验和用例间变量引证;支撑了多种定制化的调度计划,并接入到上线体系流程中;打通 HTTP 和 RPC 接口拜访,结合事务架构极大下降了接入 RPC 用例的本钱;经过用例自动化生成进一步下降用例办理本钱,快速进步了自动化测验的掩盖率。

关于旧用例体系上的数据,咱们花费了将近两周,将数千行测验代码、将近一千条校验规矩悉数迁移到新的自动化测验渠道上,得到了150+的新用例,而且校验的规矩变成了150+的 JSON Schema,不需求保护任何一行代码,就得到了比之前更完善的全字段校验规矩掩盖。

此外,咱们经过用例发现和用例生成,生成了8000+的用例,完结了读接口100%用例掩盖,并屡次辅佐发现线上反常数据问题,在用户还未感知前就现已将问题摧残在摇篮之中。

笔者认为,本文最重要的并不是对各种东西的集成和运用,100% 的用例掩盖也并非本文的终究方针。各种开源和付费东西数不胜数,只需舍得投入人力 100% 的用例掩盖也并非难事。本文真实重要的是提出了一种通用的测验结构架构,以及依据线上流量剖析得到了一种测验掩盖率的衡量计划

秉持着这种思路,上文中咱们说到的调度体系、用例履行 MQ、校验东西、测验告警体系、流量收集体系、用例生成体系,都能够依据事务灵活调整,低本钱完结大规模用例掩盖。以上是本次共享的悉数内容,假如觉得内容有用,欢迎共享转发。

-End-

原创作者|柯宗言

技能责编|许阳寅、罗国佳

BUG越改越多?微信团队用自动化测试化险为夷

各位开发者都遇到过什么样头疼的 BUG,欢迎在腾讯云开发者大众号谈论区别享评论。咱们将选取1则最有含义的共享,送出腾讯云开发者-手腕垫1个(见下图)。6月26日中午12点开奖。

BUG越改越多?微信团队用自动化测试化险为夷

BUG越改越多?微信团队用自动化测试化险为夷

BUG越改越多?微信团队用自动化测试化险为夷

BUG越改越多?微信团队用自动化测试化险为夷

BUG越改越多?微信团队用自动化测试化险为夷