2

背景

埋点对电商类app的业务发展一直有着重要的指导作用,但是其复杂的数据组成使得它的稳定性难以得到保障,往往业务逻辑的一些重构就会导致一些埋点属性甚至是整个埋点的丢失。

也正是由于埋点具有多个数据源,常规的自动化验证只能验证埋点是否存在,无法跟业务场景匹配。而对于人工排查来说,虽然能解决和场景匹配的问题,但是像不同埋点的属性之间的关联或者属性和接口字段的关联这类复杂的校验做起来也非常的困难。

本文将介绍我们是如何通过teslaLab+埋点验证平台实现了埋点的自动回归以及多维度的埋点校验,并在最近三个版本中累积发现了数十个埋点问题。

安卓

IOS

痛点

一个埋点中的数据主要由三个部分组成:

  • 接口下发数据
  • 用户行为
  • 本地运行的代码

要想验证一个埋点是否符合预期的设计,关键的难点就在于如何固定住这三个数据源,使其每次生成的结果都保持一致或者符合我们预定的规则。因此我们分别采用了以下方案来实现埋点生成时数据源的固定

  • 接口数据:对接口mock实现了对接口数据的录制&回放
  • 用户行为数据:通过谷歌的uiautomator来实现用户行为的录制&回放
  • 本地代码:和ab平台打通,通过动态调整ab配置下发接口来最大限度上固定客户端ab实验相关的代码。

概览

名词解释

  • teslaLab: 是一款无线测试工具(MAC/WIN应用),提供开箱即用的无线性能 /体验 /UI自动化 等专项测试工具。
  • ubt-verification:安卓侧实现埋点数据录制和接口Mock功能的SDK。
  • 测试场景:即校验规则组,对应着一个测试用例,包含了这个case的所有埋点校验规则
  • mock记录:包含了一个case在运行过程中请求的所有接口的数据,用于自动化回归时进行接口mock。
  • 测试记录:自动化回归的产物,即录制到的所有埋点数据。
  • 验证报告:即测试记录和测试场景的结合产物,包含了所有异常埋点的具体异常信息。
  • 任务组:来自teslaLab的概念,即测试记录的集合,一般用于聚合各业务线的测试记录。
  • 通过率:指一个测试记录中没有任何埋点异常的埋点(去重)占埋点总数(去重)的百分比。
  • 任务组通过率:指任务组中所有测试记录中没有异常的埋点总数之和(去重)占所有埋点总数之和(去重)的百分比。

系统架构图

整个埋点自动化验证平台主要由三部分组成,分别是:

  • 自动化工具:teslaLab
  • 移动端SDK:安卓的ubt-verification和ios的kylin
  • 验证平台:无线研发平台的埋点验证模块

流程图

下图为单个测试用例的首次验证流程:

主要可分为三个阶段:

  1. 准备阶段

<!---->

  • 用teslaLab录制或者编写自动化脚本
  • 在脚本编辑页手动执行脚本并选择录制Mock功能,录制接口数据并创建Mock记录
  • 新建运行任务并选中前面新建的脚本和Mock记录,执行任务之后得到一组埋点数据(即测试记录)。
  • 根据需要验证的埋点的数量自行选择手动配置(不推荐,太麻烦)或者从上一步得到的测试记录中自动生成(推荐)相应基础规则(即测试场景)。
  • 至此,埋点自动化验证的三要素(自动化脚本,Mock记录,测试场景)已集齐。

<!---->

  1. 运行阶段

<!---->

  • 可以手动执行或者通过配置Cron表达式实现定时执行在准备阶段新建的自动化任务,产出一份测试记录和一份验证报告。

<!---->

  1. 验收阶段

<!---->

  • 根据预定策略和测试记录自动生成的验证规则可能无法满足预期,因此产出的验证报告中的问题需要人工核查是否有效。

    • 如果无效则可以通过报告详情页中的修复按钮快速订正规则,或者前往测试场景详情页手动订正规则。
    • 如果有效则可以选中有效的埋点并提交报障至埋点管理平台,提交成功后会飞书通知至最后一位负责该埋点的研发和测试,当问题修复并被测试验收之后即可验收报障。
  • 至此,一个测试用例的完整验证流程结束。

详情

接下来将按照验证流程的顺序逐一介绍这三个模块的详细流程

自动化模块

埋点自动化依赖 「Tesla-lab 的自动化模块,其中数据Mock 记录的录制和UI自动化 脚本的录制依赖 「Tesla-lab」 本地编辑器,任务和任务组的定时执行依赖 「Tesla-lab」 任务调度模块,任务的实际执行依赖 「Tesla-lab」 任务执行器。

Tesla-lab 自动化流程图

Tesla-lab 自动化整体架构实现

实现上分三个模块:

<!---->

    • 编辑器端,负责脚本编辑和数据录制
    • TeslaLab 客户端负责本地脚本/ 任务的调试/执行,定时任务的部署

<!---->

  • Core-local java agent

<!---->

    • 本地核心服务,封装了基于Quartz 的任务 / 任务组
    • 本地设备管理

<!---->

      • iOS 基于 tidevice / wda
      • Android基于 uiautomator2

<!---->

    • 自动化任务实际执行者

<!---->

      • 基于pytest 的脚本执行器

<!---->

  • TeslaLab 服务

<!---->

    • 提供脚本管理
    • 任务/任务组管理
    • 设备远程管理和同步

Tesla-lab 」自动化录制编辑器

编辑器/录制器(客户端内嵌Web)

  • 录制脚本

<!---->

    • 支持手动编写或者通过点击页面生成对应代码,支持运行/暂停来调试脚本

  • 数据录制,通过和移动端SDK进行本地的socket通信,触发SDK的接口/埋点的录制/回放流程

<!---->

    • Mock接口数据录制

Tesla-lab 」自动化任务执行器

  • 回放任务创建

回放任务的创建会通过 本地socket xxxx 端口新建一个本地任务,同时会创建一份远端任务。这样可以通过Lab的安装自动同步任务到本地,TeslaLab的核心本地任务和数据都是通过agent 模块和服务端进行交互。

  • 回放任务运行

任务支持立即执行或者通过配置Cron表达式来实现定时执行,任务具有保护机制,在遇到应用crash或者anr(脚本停滞超过一定时间)时会尝试重新开始任务,并且单个任务执行总耗时超过一定时间则会提前调用结束录制的api来跳过该任务。

  • 回放任务结果

执行完毕后可通过在任务配置中开启飞书通知来获取任务组或者任务的执行结果,包含任务的执行详情。也可以直接在任务列表页点击展开查看该任务的历史执行记录,记录中包括执行期间的截图和logcat日志。

  1. 最佳实践一一个任务关联一个脚本。每个脚本关联一条Mock记录和一个测试场景。

关联mock记录和校验规则,支持模糊搜索

  1. 最佳实践二合理利用任务组,任务组可以按照产品线或者业务线进行创建。

  • 实现通过 TeslaLabEngine 单例模式实现API的收口。同时,TeslaLabEngine 可以做为独立的Jar 包接口和前端界面进行解除耦合,可以单独使用。
  1. 最佳实践三合理利用飞书通知,进行任务/任务组的排错。

报告示例:

  1. 最佳实践四」利用Cron表达式实现每日凌晨定时执行任务组,用于持续关注自动化运行的稳定性。目前一个应用的所有任务均在同一个任务组中,每天凌晨0点定时运行,其任务组的通过率即为当日的通过率指标,用于监测自动化运行的稳定性。

Tesla-lab 」演进方向

在未来,依托云真机平台实现本地调试,云端部署和运行能力,实现更加自动化/智能化的方式,再结合客户端丰富的异常采集策略将埋点缺陷,客户端缺陷尽早,尽快发现。

数据采集SDK

移动端的数据采集SDK均由teslaLab通过socket通信调起,无交互界面。

安卓侧为独立sdk:ubt-verification,ios测则是依赖于线下性能测试工具Kylin实现。

整体架构图

流程图

移动端SDK的功能主要有录制Mock数据和录制埋点数据两个流程:

准备阶段
  • 在端上通过三方工具nanoHttpPd启动服务,收到teslaLab调用之后开启mock录制流程
  • 在本地生成记录id等参数,和teslalab传参一并缓存至本地,随后重启应用
录制Mock数据
  • 重启后开始执行自动化脚本,同时将通过拦截器获得的所有的网络请求request&response详情上报至后端
  • 脚本执行完毕后teslaLab将调用相关接口停止端上的录制流程,同时SDK调用mock记录创建接口
录制埋点数据(即自动化回放)
  • 重启后开始执行自动化脚本,所有的网络请求将在收到response之后调用后端接口获取与其request匹配的mock数据并替换。
  • 所有监听到的埋点数据将被异步存入本地数据库,同时会有一个定时循环执行的任务从数据库中分批读取埋点数据并整合上报至后端。
  • 脚本执行完毕后teslaLab将调用相关接口停止端上的录制流程,同时SDK调用测试记录创建接口

埋点数据采集模块

安卓实现

主要通过反射对神策sdk实现静态代理,在代理中实现埋点上报时的回调,并在回调中将埋点数据通过eventBus抄送至负责埋点上报的管理实例中,进而存储至sqlite。后续在埋点上报阶段将分批从sqlite中取出埋点数据并通过接口上报至后端。

IOS实现

神策sdk埋点记录时有对外的通知,iOS监听神策埋点通知即可获得埋点数据。

接口Mock模块

安卓实现

通过对okhttpClientBuilder类的build方法进行插桩,并使用addInterceptor方法加入自定义拦截器,用于实现接口数据的录制和接口的Mock。这里我们是通过是否包含业务自定义的拦截器来判断当前的okhttpClient是否为我们需要插桩的目标client。

在拦截器中即可对接口数据进行录制或者是mock。

IOS实现

通过NSURProtocol对网络进行拦截,有请求发起时,请求mock服务接口判断是否有mock数据,请求到mock数据就构建响应返回mock数据,没有请求到就正常发起请求。

socket通信模块

安卓实现

通过引入三方工具nanoHttpPd,在移动端开启服务,mac客户端端即可直接通过IP+端口的方式调用到在移动端实现的get/post接口,从而实现和自动化工具teslaLab的交互。

IOS实现

通过引入三方工具GCDAsync,在iOS客户端开启服务,交互逻辑同Android。

稳定性监控模块

  • 本地记录埋点录制的总量,mock是否成功开启,包的md5值/包id等信息,跟随埋点上传至后端,用于监控自动化回归的运行稳定性以及发现问题后的排查。

遇到的挑战

在稳定运行了一个版本之后,安卓侧突然出现了大规模脚本执行失败问题,经排查运行截图定位到是商详接口的response发生数据结构的变更,导致使用前期录制的接口数据进行Mock时无法完成数据解析,自动降级切换到CDN数据,从而导致页面元素无法被脚本识别。

我们在早期有规划到这种情况,所以实现了对接口数据结构变更的自动检测以及修复提醒,但是这里的商详接口却并没有触发任何提醒,最后经排查发现我们录制到的Mock数据是风控sdk加密后的response:

初期我们是为了录制到完整的request和response数据,所以将拦截器放在了拦截器列表中最后一位。但是实际运行中应用层并不需要这些拦截器添加的额外参数,因此我们将拦截器前移到第一位,并且重新录制这些涉及商详接口的Mock记录。最终修复后脚本运行恢复正常。

平台

整体架构图

无线研发平台:

  • 测试场景模块
  • Mock记录模块
  • 测试记录模块
  • 验证报告模块

埋点管理平台

  • 埋点报障模块

流程图

埋点验证&验收流程
  1. 通常一个测试用例中的埋点都有几十个,因此推荐在构建场景时直接从测试记录生成默认规则。
  2. 通过生成的规则产出的验证报告的通过率一般在50~70%,这是因为部分埋点属性的必传属性不是固定的,通常在不同的场景会有着不同的表现。但是生成此规则的时候是根据埋点管理平台返回的埋点信息生成的,因此需要将这部分规则进行订正,可以在验证报告详情页中通过快捷键直接一键修复,或者手动进行调整。
  3. 订正之后重新进行校验,产出的报告通过率通常会提升至75%左右,剩下的异常部分通常由一些随机因素导致(例如时间戳,页面浏览时长)。此时剩余问题较少,可以直接人工尝试复现,经确认后即可选择埋点并提交报障至埋点管理平台。
  4. 埋点管理平台将发送飞书通知至最近负责过此埋点的研发,研发修复问题之后会飞书通知最近负责过的测试,经测试和报障发起人验收之后报障流程结束。如果问题在修复或者验收阶段停留超过一定时间将上升并飞书通知负责人leader。
最佳实践

根据最近几个版本的用户反馈和总结,目前的最佳实践为:

  1. 从测试记录自动生成基础规则,并用于产出验证报告。
  2. 根据报告结果,在报告详情中使用快捷键对必传规则进行快速修正,并再次触发验证,得到报告。
  3. 直接尝试人工复现报告中剩余的问题,确认后直接提交报障至埋点管理平台。

测试场景模块

为了从多方面对埋点实现充分的精准校验,我们根据埋点的内容将用于验证的规则划分为埋点维度和属性维度:

  • 埋点维度

<!---->

    • 埋点的数量
    • 埋点之间的时序关系
    • 是否重复上报(1s内上报多次)

<!---->

  • 属性维度

<!---->

    • 是否必传
    • 是否可空
    • 取值范围

<!---->

      • 枚举类型
      • 多层嵌套JSON数组
      • 数字区间

<!---->

    • 属性值之间的映射(例如tabId和tabName)
    • 正则校验
    • 和mock记录中接口数据的映射(例如金刚位的埋点有部分属性的值来源于接口下发)

并且每一个规则都可选强/弱校验,区别只在于弱校验不会影响到最终的通过率指标

遇到的挑战
  1. 一个运行时长1分钟的测试用例,假设能够录制到100个左右的埋点,去重后大约40个,每个埋点需要验证的属性平均约7个,这意味着每一个case都需要手动点击4073*3=2520次才能完成基础规则的配置,这显然是不可接受的,所以我们实现了从录制的埋点数据中按照预定策略自动生成默认规则,这样只需要保证录制的埋点数据正常,即可完成最基本规则(包括埋点数量,是否必传,是否可空和取值范围规则)的构建。

  1. 对于首页的一些的曝光埋点来说,大部分case都会上报,并且其规则基本一致,所以为了避免重复配置浪费人力,我们实现了全局场景,并按照配置的优先级和普通规则进行整合,目前已用于首页曝光埋点的接口映射规则配置。

  1. 部分埋点属性的值是直接将算法下发的数据和接口数据合并的结果,这类数据一般为嵌套层级较深的JSON数组,由于数组中元素是无序的,因此枚举类型不能满足其取值范围的校验。为此我们通过递归将问题简化为JSON对象之间的对比来寻找差异,实现了针对这类复杂层级JSON数组的规则提取,规则校验和规则修复。
  2. 埋点的详情数据均来自埋点管理系统,自动生成的规则也是根据埋点管理平台中埋点属性是否必传的标注来生成,但是大部分埋点属性是否必传并非绝对情况,在不同的应用场景下可能必传也可能不必传,这导致我们产出的验证报告中一直有大量噪声。为此我们实现了在报告中对规则快速修复以及接口映射规则,在迅速消除噪声的同时也建立起埋点数据和接口数据的映射关系,从根源上对埋点数据进行校验。虽然在修复上线后由于商详接口的问题整体的验证报告通过率并未大幅度提升,但是在修复后的单个版本就发现了数十个埋点问题。

Mock记录模块

  • mock记录由埋点数据采集sdk创建,在创建时检查必传接口(ab配置下发接口,首页的一些接口)是否录制来进行卡口,避免后续产出的报告为脏数据。

  • 为了灵活控制mock的实际数据,每一个网络请求都会被录制,并且支持开关。对于get请求还支持根据requestParams来自动匹配对应的response。

  • 为了协助自动化工具解决弹窗的问题,我们还实现了全局Mock,统一将红包弹窗的接口mock并返回空结果。

  • 接口数据作为埋点数据的重要组成部分,往往决定着自动化回归的稳定性,因此我们还实现了对接口变更的自动检测,通过比对接口真实数据和mock数据的key值,寻找数据结构上的差异,生成变更的记录和修改建议。

并且支持变更的应用和撤回。

遇到的挑战
  1. 在录制mock数据初期,我们遇到过部分带有搜索商品操作的脚本会执行失败,经排查发现是端上的实验组代码已经调整,而ab配置下发的接口由于mock已经被固定,这导致原先录制的脚本无法适配新的实验组的代码。为此我们引入了事件变更监听,并过滤出所有来自ab管理系统的变更事件,再依据事件详情中的ab实验id去ab管理系统获取最新的配置信息,来自动生成用于批量更新mock数据的推荐修改结果。

测试记录

  • 测试记录列表页

测试记录由移动端数据采集sdk创建,会根据端上上传的监控信息进行比对,如果有异常会飞书报警。

其中红色背景表示记录异常,完全不可信:

  • mock未开启:即获取mock数据的接口发生过非200的情况,说明mock未开启或者失效。
  • 埋点数量不一致:后端通过埋点上报接口统计到的埋点数量和端上在神策sdk的回调处统计的数量不一致,说明sdk存在漏报或者上报接口异常问题。

淡黄色背景表示记录可能有异常,需要排查:

  • 即测试记录中的埋点数量和录制的时长比例异常,正常比例约大于1:1,如果出现埋点数量小于录制时长秒数,一般为脚本异常,需要人工排查。

  • 测试记录详情页

验证报告模块

  • 报告的概览会展示报告的基础信息:

  • 报告的详情按照埋点聚合,展示了每一个埋点的异常信息及其原始数据。

  • 在报告中经人工复现改埋点异常之后即可选择异常的埋点并提交报障至埋点管理平台,报障的流程与线上报障一致,支持问题打回,测试复测,创建人验收等功能,并且在超时后会自动上升并发送飞书通知至leader。

总结

埋点验证平台在每个阶段遇到的痛点其实都是“埋点数据如何固定”这个问题的一个缩影。业务的快速迭代使得埋点验证过程中积累的mock记录和验证规则这些沉淀产物十分脆弱,即使我们通过多种手段实现了这些数据的手动或自动更新,它们最终也难免会彻底失效。因此我们后续在提取长期有效沉淀的同时,也将致力于提升验证规则和mock数据构建的自动化程度,来压缩失效后数据重建的人力成本。

文/ZHOUXIAOFENG

关注得物技术,做最潮技术人!


得物技术
846 声望1.5k 粉丝