共计 6338 个字符,预计需要花费 16 分钟才能阅读完成。
1. 前言
作为一名自信的 QA,对于测试通过的我的项目,如果有人反馈有问题,脑海中的第一反馈肯定就是:不可能!肯定是操作有问题。入职以来经手大大小小的我的项目也有 40 多个,始终没出过问题,也让我在年度的总结上自信地写到:所有我的项目按时按质发版,未呈现线上问题。
然而,这种自信让我漫不经心,使得微信小程序 SDK 的第一个线上问题也随之而来了。
对于线上问题,可能很多人都认为把问题解决了就完事了,并不器重对问题的复盘。事实上,复盘的作用可能远大于解决问题自身。
在神策的企业文化中重要的一项就是复盘,每一个问题对于咱们来说都是一笔贵重的财产。通过对于问题的复盘,总结经验教训,可能更好地促成咱们成长。
上面咱们来看下对于这个问题是如何进行复盘的。
2. 回顾指标
神策微信小程序 SDK 的指标是实现对于支流小程序开发框架的全埋点性能。然而,在测试过程中发现因为 Taro3.0 框架从新定义了标签点击行为的逻辑,使得一次点击行为会触发 SDK 的两次点击事件 $MPClick,造成了埋点数据反复。
因而,这次公布的 v1.14.3 版本旨在解决 Taro3.0 框架下点击事件反复触发的问题,实现神策微信小程序 SDK 真正意义上的无框架阻碍全埋点采集。
3. 评估后果
3.1. 合乎指标
- 解决了 Taro3.0 框架下点击事件反复触发的问题;
- 实现了神策微信小程序 SDK 真正意义上的无框架阻碍全埋点采集。
3.2. 低于指标
- 本次公布的版本存在重大的线上问题。
4. 剖析起因
4.1. 回顾过程
- 2020 年 12 月 17 日 19 : 05 微信小程序 SDK 公布了 v1.14.3 版本,新增了 $MPClick 事件可自定义属性,修复了 Taro3.0 框架下点击事件反复触发的问题;
- 2020 年 12 月 18 日 16 : 27 技术顾问收到客户反馈:微信小程序 SDK 更新到 v1.14.3 版本后,测试过程中发现 SDK 篡改了他们办法的返回值,属于破坏型 proxy;
- 2020 年 12 月 18 日 16 : 30 技术顾问查看代码发现这个问题以前在支付宝小程序呈现过,并和 QA 一起复现了问题;
- 2020 年 12 月 18 日 16 : 35 问题同步给研发和 QA 组长,并调配下一步具体工作:QA 去 GitHub 上删除对应版本代码,研发组长帮助删除 npm 上的版本,研发开始修复问题;
- 2020 年 12 月 18 日 16 : 37 研发组长和 QA 实现版本删除;
- 2020 年 12 月 18 日 16 : 45 研发修复实现,交由 QA 测试;
- 2020 年 12 月 18 日 18 : 05 QA 测试实现,并公布了最新的修复版本 v1.14.4,实现了线上验证;
- 2020 年 12 月 19 日 11 : 31 技术顾问组长找出了所有应用问题版本 v1.14.3 的客户并同步给技术顾问;
- 2020 年 12 月 19 日 15 : 00 技术顾问告诉了所有应用 v1.14.3 的客户,告知他们存在的问题并揭示他们更新版本。
整个问题的生命周期从 2020 年 12 月 17 日 19 : 05 发版,到 2020 年 12 月 19 日 15 : 00 所有客户告诉实现,总共历时 44 个小时。能够分为如图 4-1 中的 6 个阶段:
图 4-1 线上问题生命周期
这次问题是我经验的第一个线上问题,这次经验不仅让我残缺理解到线上问题的解决流程,更让我充沛地感触到各个环节上团队人员的密切配合。从问题开始到问题解决,小程序团队全员时刻处于待命状态,每个环节都争分夺秒,确保了此次问题的疾速修复,没有造成较大的影响。
回顾线上问题的整个过程之后,咱们须要剖析产生这个问题的具体起因。
4.2. 起因剖析
4.2.1. 主动采集点击事件
在进行具体的起因剖析之前,咱们先来看下神策微信小程序 SDK 主动采集点击事件的原理。
1、在重写 Page 函数时,先通过 _.getMethods 获取除 Page 钩子以外的自定义事件处理函数汇合 methods:
`Page =` `function` `(option) {`
`//` ` 先判断 mpClick 是否配置主动采集,若配置为真则获取自定义办法并代理重写 `
`var methods = sa.para.autoTrack && sa.para.autoTrack.mpClick && _.getMethods(option);`
`if``(!!methods) {`
`for``(var i = 0, len = methods.length; i < len; i++) {`
`//` ` 对 methods 汇合的每一个自定义事件处理函数进行重写 `
`click_proxy(option, methods[i]);`
`}`
`}`
`}`
`//` `_.getMethods 办法,获取用户自定义的所有事件 `
`_.getMethods =` `function``(option) {`
`var methods = [];`
`for` `(var m` `in` `option) {`
`if` `(typeof(option[m])===``'function'` `&& !mpHook[m]) {`
`methods.push(m);`
`}`
`}`
`return` `methods;`
`}`
2、对 methods 汇合的每一个自定义事件处理函数进行重写,获取事件触发时的 type 类型,type 为 tap、longpress 或者 longtap 则触发 $MPClick 事件,将 wxml 文件标签中 dataset 定义的属性作为事件属性:
`//` ` 点击事件代理处理函数 `
`function` `click_proxy(option, method) {`
`var oldFunc = option[method];`
`option[method] =` `function``() {`
`//` ` 在重写 oldFunc 之前就曾经判断是一个办法类型,此处是做一次反复的校验 `
`var res = oldFunc.apply(this, arguments);`
`var prop = {},`
`type` `=` `''``;`
`if``(_.isObject(arguments[0])) {`
`//` ` 将 wxml 标签中 dataset 定义的属性作为事件属性 `
`var dataset = current_target.dataset || {};`
`type` `= arguments[0][``'type'``];`
`prop[``'$element_id'``] = current_target.``id``;`
`prop[``'$element_type'``] = dataset[``'type'``];`
`prop[``'$element_content'``] = dataset[``'content'``];`
`prop[``'$element_name'``] = dataset[``'name'``];`
`}`
`if``(``type` `&& _.isClick(``type``)) {`
`prop[``'$url_path'``] = _.getCurrentPath();`
`sa.track(``'$MPClick'``, prop);`
`}`
`return` `res;`
`}`
`};`
`//` ` 点击类型判断办法 `
`_.isClick =` `function``(``type``) {`
`var mpTaps = {`
`"tap"``: 1,`
`"longpress"``: 1,`
`"longtap"``: 1,`
`};`
`return` `!!mpTaps[``type``];`
`}`
点击事件的主动采集不仅能采集到用户的点击行为,还能主动采集点击标签的相干属性。
只有在 wxml 文件的标签中通过 data- 定义的属性都能够采集到,能够主动采集的属性如表 4-1 所示:
表 4-1 主动采集的属性
倡议在元素中定义 id、data-content、data-name 这三个元素之一作为元素标识,若无这三个属性,则在神策剖析平台无奈进行标识。
接下来,咱们来看一个主动采集点击事件的例子。
1、配置如下的 button 标签:
`<button bindtap=``"test"` `data-name=``"button"` `id``=``"button"` `data-content=``'button'` `data-``type``=``"button"``> 测试 <``/button``>`
2、点击 button 后触发的事件内容如下所示:
`{`
`"distinct_id"``:``"1610349175397-726909-0e567a51188708-20891891"`
`"lib"``:{`
`"$lib"``:``"MiniProgram"`
`"$lib_method"``:``"code"`
`"$lib_version"``:``"1.14.4"`
`}`
`"properties"``:{`
`"$lib"``:``"MiniProgram"`
`"$lib_version"``:``"1.14.4"`
`"$network_type"``:``"wifi"`
`"$manufacturer"``:``"devtools"`
`"$model"``:``"iPhone 6/7/8 Plus"`
`"$screen_width"``:414`
`"$screen_height"``:736`
`"$os"``:``"devtools"`
`"$os_version"``:``"10.0.1"`
`"$timezone_offset"``:-480`
`"$app_id"``:``"wx82a49f7cb5547449"`
`"$url_path"``:``"pages/index/index"`
`"$element_id"``:``"button"`
`"$element_type"``:``"button"`
`"$element_content"``:``"button"`
`"$element_name"``:``"button"`
`"$is_first_day"``:``false`
`"$ip"``:``"117.71.111.48"`
`"$browser"``:``"WeChat"`
`"$browser_version"``:``"7.0.4"`
`"$is_login_id"``:``false`
`"$city"``:``"合肥"`
`"$province"``:``"安徽"`
`"$country"``:``"中国"`
`}`
`"anonymous_id"``:``"1610349175397-726909-0e567a51188708-20891891"`
`"type"``:``"track"`
`"event"``:``"$MPClick"`
`"time"``:1615194119222`
`"is_login_id"``:``false`
`"map_id"``:``"1610349175397-726909-0e567a51188708-20891891"`
`"user_id"``:-8183290914376425000`
`"recv_time"``:1615194119222`
`"project"``:``"gongcheng"`
`}`
- 至此,咱们能够看到主动采集了 button 的点击事件。
4.2.2. 具体起因
理解了微信小程序 SDK 是如何实现主动采集点击事件的原理,此次问题的起因就比拟容易剖析了,上面咱们看下导致此次问题的具体起因是什么。
1、首先咱们须要理解下小程序的页面逻辑,每个页面都有一个独自的 JS 文件为页面组件增加执行逻辑,所有办法都写在 Page({} ) 中,次要蕴含三个局部:页面的初始数据,小程序自身带有的生命周期函数和自定义的函数办法。例如上面示例中定义的两个办法 testA 和 testB:
`Page({`
`/**`
`* 页面的初始数据 `
`*/`
`data: {`
`},`
`/**`
`* 生命周期函数 -- 监听页面加载 `
`*/`
`onLoad:` `function` `(options) {`
`},`
`/**`
`* 自定义办法 testA`
`*/`
`testA:` `function` `() {`
`console.log(``'执行办法 B'``,this.B())`
`},`
`/**`
`* 自定义办法 testB`
`*/`
`testB:` `function` `() {`
`return` `'执行办法 B'`
`}`
`})`
2、依据上一节提到的点击事件主动采集原理,咱们对客户小程序的所有自定义办法进行了重写代理,判断 type 类型为点击时触发 $MPClick 事件,但前提肯定是不能影响客户自定义办法的执行;
3、小程序 SDK v1.14.3 版本在更新现有逻辑时,批改了代理办法的返回值,由返回客户办法的执行后果改成了间接返回 false,如图 4-2 所示:
图 4-2 小程序 SDK v1.14.3 版本代码 diff 图
4、这就使得下面代码中 Page 自定义的办法 testB(),本来客户业务逻辑是“return ‘ 执行办法 B’”,然而通过咱们 SDK 的办法重写,变成了“return false”。
5、testA() 原本应该打印出 testB() 中定义的返回值,不过因为 SDK 代理使得 testB() 返回 false,导致 testA() 的执行后果不合乎预期,如图 4-3 所示:
图 4-3 testA() 的谬误执行后果
正确的业务逻辑执行后果应该如图 4-4 所示:
图 4-4 testA() 的正确执行后果
4.2.3. 解决方案
晓得了问题的起因之后,解决问题就比拟容易了。只须要在代理客户办法时批改返回值为客户原来办法的返回值,如图 4-5 所示:
5. 总结法则
5.1. 经验教训
尽管此次线上问题的起因比较简单,然而通过粗浅的检查之后,我总结了如下几点经验教训:
- QA 对代码的改变须要具备敏锐的感知,要详尽查究每行代码改变的目标。对于代码的 diff,肯定要知其然,知其所以然。QA 可能不须要编写很简单的代码,然而肯定要能看懂代码,否则测试覆盖率肯定不高;
- 此次问题的产生次要在于 QA 的测试 case 未笼罩到办法的返回值,对于小程序基本原理了解的不够深刻。因而,QA 须要对测试的业务十分相熟,通过业务属性摸索测试 case;
- QA 没有恪守流程,在研发组长没有给出 code review 通过的回复之前,就间接开始测试。这样使得测试代码是未通过 double check 的,很容易呈现问题;
- 此次问题属于再犯,之前在其余小程序上也呈现过同样的问题。这种再犯的问题是最可怕的,表明了没有对呈现的问题做好总结。对于 QA 而言,须要将漏测的 case 退出回归测试的 case 中,定期对回归测试的 case 进行总结。
5.2. 问题
通过此次线上问题裸露了本人作为一名 QA 所存在的一些问题:
- 对于 SDK 代码和小程序基本原理还不够相熟;
- 对于代码的改变没有追本溯源,相干逻辑的理解不够充沛;
- 没有严格遵守测试流程;
- 没有及时总结已有的问题,导致同样的问题再次出现。
5.3. 改良
通过此次线上问题的复盘,有如下口头作为改良的方向:
- QA 在 2021 年的 Q1 季度实现对微信小程序 SDK 代码的熟读,研发负责组织 2 次以上针对 QA 的小程序基本原理培训,从而让 QA 和研发在代码了解上达到程度统一;
- 研发在当前我的项目中须要具体评估改变,精确定位影响范畴,并在相应的提测邮件上重点备注,让 QA 能更详尽的设计测试 case;
- 此次问题之后,QA 和研发都须要严格遵守开发测试流程,互相监督,绝不在任何一个流程环节中呈现越界或者违规;
- QA 在 2020 年 12 月底之前实现对此次问题的具体总结,并把漏测的 case 退出到回归测试 case 中,避免再次出现同样的问题。
6. 结语
本文通过对于一次线上问题的复盘,介绍了复盘的整体流程,心愿通过本文能给大家提供一些复盘相干的参考。