货拉拉App录制回放的探索与实践

作者简介:徐卓毅Joe,来自货拉拉/技术中心/质量保障部,专注于移动测试效能方向。

一、背景与目标

近些年货拉拉的业务持续高速发展,为了满足业务更短周期、更高质量交付的诉求,从今年开始我们的移动App的迭代交付模型也从双周演化为单周。因此,在一周一版的紧张节奏下,随之而来的对测试质量保障的挑战也日益增加,首当其冲要解决的就是如何降低移动App每周版本回归测试的人力投入。

早期我们尝试过基于Appium框架编写UI自动化测试脚本,并且为了降低编写难度,我们也基于Appium框架进行了二次开发,但实践起来依然困难重重,主要原因在于:

  1. 上手和维护成本高

    • 需要掌握一定基础知识才能编写脚本和排查过程中遇到的问题;
    • 脚本编写+调试耗时长,涉及的元素定位+操作较多,调试要等待脚本执行回放才能看到结果;
    • 排查成本高,由于UI自动化测试的稳定性低,需投入排查的脚本较多,耗时长;
    • 维护成本高,每个迭代的需求改动都可能导致页面元素或链路调整,需不定期维护;
  2. 测试脚本稳定性低

    • 容易受多种因素(服务端环境、手机环境等)影响,这也造成了问题排查和溯源困难;
    • 脚本本身的稳定性低,模拟手工操作的方式,但实际操作点击没有那么智能;
      • 脚本识别元素在不同分辨率、不同系统版本上,识别的速度及准确度不同;
      • 不同设备在某些操作上表现,例如缩放(缩放多少)、滑动(滑动多少)有区别;
      • 由于功能复杂性、不同玩法的打断(如广告、弹窗、ab实验等);

所以,在App UI自动化测试上摸爬滚打一段时间后,我们积累了大量的踩坑经验。但这些经验也让我们更加明白,如果要大规模推行App UI自动化测试,必须要提高自动化ROI,否则很难达到预期效果,成本收益得不偿失。

我们的目标是打造一个低成本、高可用的App UI自动化测试平台。它需要满足如下条件:

  1. 更低的技术门槛:上手简单,无需环境配置;
  2. 更快的编写速度:无需查找控件,手机上操作后就能生成一条可执行的测试脚本;
  3. 更小的维护成本: 支持图像识别,减少由于控件改动导致的问题;
  4. 更高的稳定性: 回放识别通过率高,降低环境、弹窗的影响;
  5. 更好的平台功能: 支持脚本管理、设备调度、测试报告等能力,提升执行效率,降低排查成本;

二、行业方案

考虑到自动化ROI,我们基本确定要使用基于录制回放方式的自动化方案,所以我们也调研了美团、爱奇艺、字节、网易这几个公司的测试工具平台的实现方案:

  1. 网易Airtest是唯一对外发布的工具,但免费版本是IDE编写的,如果是小团队使用该IDE录制UI脚本来说还是比较方便的,但对于多团队协同,以及大规模UI自动化的实施的需求来说,其脚本管理、设备调度、实时报告等平台化功能的支持还不满足。
  2. 美团AlphaTest上使用的是App集成SDK的方式,可以通过底层Hook能力采集到操作数据、网络数据等更为详尽的内容,也提供了API支持业务方自定义实现,如果采用这种方案,移动研发团队的配合是很重要的。
  3. 爱奇艺的方案是在云真机的基础上,使用云IDE的方式进行录制,重点集成了脚本管理、设备调度、实时报告等平台化功能,这种方案的优势在于免去开发SDK的投入,可以做成通用能力服务于各业务App。
  4. 字节SmartEye也是采用Android集成SDK,iOS监听系统事件流的方式,其工具本身更聚焦精准测试的能力建设,而精准测试当前货拉拉也在深入实践中,后续有机会我们再详细介绍。

综上分析,如果要继续推行App UI自动化测试,我们也需要自研测试平台,最好是能结合货拉拉现有的业务形态和能力优势,用最低的自研方案成本,快速搭建起适合我们的App录制回放测试平台,这样就能更快推动实践,降低业务测试当前面临的稳定性保障的压力。

三、能力建设

货拉拉现有的能力优势主要有:

  1. 货拉拉的云真机建设上已有成熟的经验(感兴趣的读者可参见文章《货拉拉云真机平台的演进与实践》);
  2. 货拉拉在移动App质效上已有深入实践,其移动云测平台已沉淀了多维度的自动化测试服务(如性能、兼容性、稳定性、健壮性、遍历、埋点等),具备比较成熟的平台能力。

因此,结合多方因素,最终我们选择了基于云真机开展App UI录制回放的方案,在借鉴其他公司优秀经验的基础上,结合我们对App UI自动化测试过程中积累的宝贵经验,打造了货拉拉App云录制回放测试平台。

下面我们会按录制能力、回放能力、平台能力三大部分进行介绍。

3.1 录制能力

录制流程从云真机的操作事件开始,根据里面的截图和操作坐标解析操作的控件,最终将操作转化为脚本里的单个步骤。并且支持Android和iOS双端,操作数据上报都是用旁路上报的方式,不会阻塞在手机上的操作。

下面是我们当前基于云真机录制的效果:

在录制的过程中,其目标主要有:

  1. 取到当前操作的类型 点击、长按、输入、滑动等;
  2. 取到操作的目标控件 按钮、标签栏、文本框等;

3.1.1 云真机旁路上报&事件解析

首先要能感知到用户在手机上做了什么操作,当我们在页面上使用云真机时,云真机后台可以监控到最原始的屏幕数据,不同操作的数据流如下:

r 复制代码
// 点击
d 0 10 10 50
c
u 0
c
// 长按
d 0 10 10 50
c
<wait in your own code>
u 0
c
// 滑动
d 0 0 0 50
c
<wait in your own code>  //需要拖拽加上等待时间
m 0 20 0 50
c
m 0 40 0 50
c
m 0 60 0 50
c
m 0 80 0 50
c
m 0 100 0 50
c
u 0
c

根据协议我们可以判断每次操作的类型以及坐标,但仅依赖坐标的录制并不灵活,也不能实现例如断言一类的操作,所以拿到控件信息也非常关键。

一般UI自动化中会dump出控件树,通过控件ID或层级关系定位控件。而dump控件树是一个颇为耗时的动作,普通布局的页面也需要2S左右。

如果在录制中同时dump控件树,那我们每点击都要等待进度条转完,显然这不是我们想要的体验。而可以和操作坐标一起拿到的还有手机画面的视频流,虽然单纯的截图没有控件信息,但假如截图可以像控件树一样拆分出独立的控件区域,我们就可以结合操作坐标匹配对应控件。

3.1.2 控件/文本检测

控件区域检测正是深度学习中的目标检测能解决的问题。

这里我们先简单看一下深度学习的原理以及在目标检测过程中做了什么。

深度学习原理

深度学习使用了一种被称为神经网络的结构。像人脑中的神经元一样,神经网络中的节点会对输入数据进行处理,然后将结果传递到下一个层级。这种逐层传递和处理数据的方式使得深度学习能够自动学习数据的复杂结构和模式。

总的来说,深度学习网络逐层提取输入的特征,总结成更抽象的特征,将学习到的知识作为权重保存到网络中。

举个例子,如果我们使用深度学习来学习识别猫的图片,那么神经网络可能会在第一层学习识别图片中的颜色或边缘,第二层可能会识别出特定的形状或模式,第三层可能会识别出猫的某些特征,如猫的眼睛或耳朵,最后,网络会综合所有的特征来确定这张图片是否是猫。

目标检测任务

目标检测是深度学习中的常见任务,任务的目标是在图像中识别并定位特定物体。

在我们的应用场景中,任务的目标自然是UI控件:

  1. 识别出按钮、文本框等控件,可以归类为图标、图片和文本;
  2. 圈定控件的边界范围;

这里我们选用知名的YOLOX目标检测框架,社区里也开放许多了以UI为目标的预训练模型和数据集,因为除了自动化测试外,还有通过UI设计稿生成前端代码等应用场景。

下图是使用公开数据集直接推理得到的控件区域,可以看出召回率不高。这是因为公开数据集中国外APP标注数据更多,且APP的UI风格不相似。

预训练和微调模型

而最终推理效果依赖数据集质量,这需要我们微调模型。由于目标数据集相似,所以我们只需要在预训练模型基础时,冻结骨干网络,重置最后输出层权重,喂入货拉拉风格的UI数据继续训练,可以得到更适用的模型。

ini 复制代码
model = dict (backbone=dict (frozen_stages=1 # 表示第一层 stage 以及它之前的所有 stage 中的参数都会被冻结 )) 

通过目标检测任务,我们可以拿到图标类的控件,控件的截图可以作为标识存储。当然,文本类的控件还是转化成文本存储更理想。针对文本的目标检测任务不仅精准度更高,还能提供目标文本的识别结果。我们单独用PaddleOCR再做了一次文本检测识别。

3.1.3 脚本生成

所有操作最终都会转化为脚本储存,我们自定义了一种脚本格式用来封装不同的UI操作。

以一次点击为例,操作类型用Click()表示;如果是点击图标类控件,会将图标的截图保存(以及录制时的屏幕相对坐标,用于辅助回放定位),而点击文案则是记录文本。

操作消抖: 点击、长按和滑动之间通过设置固定的时长消除实际操作时的抖动,我们取系统中的交互动效时长,一般是200~300ms。

文本输入: 用户实际操作输入文本时分为两种情况,一是进入页面时自动聚焦编辑框,另一种是用户主动激活编辑,都会拉起虚拟键盘。我们在回放时也需要在拉起键盘的情况下输入,才能真实还原键盘事件对页面的影响。

shell 复制代码
am broadcast -a ADB_INPUT_B64 --es msg "xxx"

目标分组: 一个页面上可能有多个相同的图标或文案,所以在录制时会聚合相同分组,在脚本中通过下标index(0)区分。

3.2 回放能力

回放脚本时,则是根据脚本里记录的控件截图和文本,匹配到回放手机上的目标区域,进而执行点击、滑动等操作。这里用到的图像和文本匹配能力也会用在脚本断言里。

回放效果见下图:

3.2.1 图像匹配

与文本相比,图标类控件在回放时要应对的变化更多:

  • 颜色不同;
  • 分辨率不同
  • 附加角标等提示;

在这种场景中,基于特征点匹配的SIFT算法很合适。

尺度不变特征变换(Scale-invariant feature transform, SIFT)是计算机视觉中一种检测、描述和匹配图像局部特征点的方法,通过在不同的尺度空间中检测极值点或特征点(Conrner Point, Interest Point),提取出其位置、尺度和旋转不变量,并生成特征描述子,最后用于图像的特征点匹配。

对图像做灰度预处理之后能减少颜色带来的噪音,而SIFT的尺度不变特性容忍了分辨率变化,附加的角标不会影响关键特征点的匹配。

除此之外,为了减低误匹配,我们增加了两个操作:

RegionMask:在匹配之前,我们也做了控件检测,并作为遮罩层Mask设置到SIFT中,排除错误答案之后的特征点更集中稳定。

屏蔽旋转不变性:因为不需要在页面上匹配旋转后的目标,所以我们将提取的特征点向量角度统一重置为0。

java 复制代码
  sift.detect(image, kpVector, mask);
  // 设置角度统一为0,禁用旋转不变性
  for (int i = 0; i < kpVector.size(); i++) {
      KeyPoint point = kpVector.get(i);
      point.angle(0);
      ...
  }
  sift.compute(image, kpVector, ret);

3.2.2 文本匹配

文本匹配很容易实现,在OCR之后做字符串比较可以得到结果。

但是因为算法本身精准度并不是百分百(OCR识别算法CRNN精准度在80%),遇到长文案时会出现识别错误,我们通过计算与期望文本间的编辑距离容忍这种误差。

但最常见的还是全角和半角字符间的识别错误,需要把标点符号作为噪音去除。

还有另一个同样和长文案有关的场景:机型宽度不同时,会出现文案换行展示的情况,这时就不能再去完整匹配,但可以切换到xpath使用部分匹配

perl 复制代码
//*[contains(@text,'xxx')]

3.2.3 兜底弹窗处理

突然出现的弹窗是UI自动化中的一大痛点,无论是时机和形式都无法预测,造成的结果是自动化测试中断。

| |

弹窗又分为系统弹窗和业务弹窗,我们有两种处理弹窗的策略:

  1. Android提供了一个DeviceOwner角色托管设备,并带有一个策略配置(PERMISSION_POLICY_AUTO_GRANT),测试过程中APP申请权限时天宫管家自动授予权限;
  1. 在自动化被中断时,再次检查页面有没有白名单中的弹窗文案,有则触发兜底逻辑,关闭弹窗后,恢复自动化执行。

3.2.4 动装包授权

Android碎片化带来的还有不同的装包验证策略,比如OPPO&VIVO系机型就需要输入密码才能安装非商店应用。

为了保持云真机的环境纯净,我们没有通过获取ROOT授权的方式绕过,而是采用部署在云真机内置的装包助手服务适配了不同机型的装包验证。

3.2.5 数据构造&请求MOCK

目前为止我们录制到的还只有UI的操作,但场景用例中缺少不了测试数据的准备。 首先是测试数据构造,脚本中提供一个封装好的动作,调用内部平台数据工厂,通过传入和保存变量能在脚本间传递调用的数据。

同时脚本还可以关联到APP-MOCK平台,在一些固定接口或特定场景MOCK接口响应。譬如可以固定AB实验配置,又或是屏蔽推送类的通知。

3.1 平台能力

3.3.1 用例编辑&管理

有实践过UI自动化的人应该有这种感受,在个人电脑搭建一套自动化环境是相当费劲的,更不用说要同时兼顾Android和iOS。

当前我们已经达成了UI自动化纯线上化这一个小目标,只需要在浏览器中就可以完成UI脚本的编辑、调试和执行。现在正完善更多的线上操作,以Monaco Editor为基础编辑器提供更方便的脚本编辑功能。

3.3.2 脚本组&任务调度

为了方便管理数量渐涨的用例,我们通过脚本组的方式分模块组织和执行脚本。每个脚本组可以设置前后置脚本和使用的帐号类别,一个脚本组会作为最小的执行单元发送到手机上执行。

我们可以将回归场景拆分成若干个组在多台设备上并发执行,大大缩短了自动化用例的执行时间。

四、效果实践

4.1 回归测试提效

App录制回放能力建设完毕后,我们立即在多个业务线推动UI自动化测试实践。我们也专门成立了一支虚拟团队,邀请各团队骨干加入,明确回归测试提效的目标,拉齐认知,统一节奏,以保障UI自动化的大规模实践的顺利落地。

  1. 建立问题同步及虚拟团队管理的相关制度,保障问题的快速反馈和快速解决。

  2. 制定团队的UI测试实践管理规范,指导全体成员按统一的标准去执行,主要包括:

    • 回归用例筛选:按模块维度进行脚本转化,优先覆盖P0用例(占比30%左右);
    • 测试场景设计:设计可以串联合并的场景,这样合并后可提升自动化执行速度;
    • 测试数据准备:自动化账号怎么管理,有哪些推荐的数据准备方案;
    • 脚本编写手册:前置脚本、公共脚本引入规范、断言规范等;
    • 脚本执行策略:脚本/脚本组管理及执行策略,怎样能执行的更快;

所以,我们在很短的时间内就完成了P0回归测试用例的转化,同时我们还要求:

  1. 回放通过率必须高于90%,避免给业务测试人员造成额外的干扰,增加排查工作量;
  2. 全量场景用例的执行总时长要小于90分钟,充分利用云真机的批量调度能力,快速输出测试报告。而且某种程度来说,还能避开因服务端部署带来的环境问题的影响;

截止目前,我们已经支持10多次单周版本的回归测试,已经可以替代部分手工回归测试工作量,降低测试压力的同时提升了版本发布质量的信心。

4.2 整体测试效能提升

在App UI自动化测试的实施取得突破性进展后,我们开始尝试优化原有性能、兼容、埋点等自动化测试遇到的一些问题,以提升移动App的整体测试效能。

  • App性能自动化测试: 原有的性能测试脚本都是使用基于UI元素定位的方式,每周的功能迭代都或多或少会影响到脚本的稳定性,所以我们的性能脚本早期每周都需要维护。而现在的性能测试脚本通过率一般情况下都是100%,极个别版本才会出现微调脚本的情况。
  • App深度兼容测试: 当涉及移动App测试时,兼容性测试的重要性不言而喻。移动云测平台在很早就已支持了标准兼容测试能力,即结合智能遍历去覆盖更多的App页面及场景,去发现一些基础的兼容测试问题。但随着App UI自动化测试的落地,现在我们已经可以基于大量的UI测试脚本在机房设备上开展深度兼容测试。

  • App 埋点 自动化测试: 高价值埋点的回归测试,以往我们都需要在回归期间去手工额外去触发操作路径,现在则基于UI自动化测试模拟用户操作行为,再结合移动云测平台已有的埋点自动校验+测试结果实时展示的能力,彻底解放人力,实现埋点全流程自动化测试。
  • 接入 CICD 流水线: 我们将核心场景的UI回归用例配CICD流水线中,每当代码合入或者触发构建后,都会自动触发验证流程,如果测试不通过,构建人和相关维护人都能立即收到消息通知,进一步提升了研发协同效率。

五、未来展望

"道阻且长,行则将至,行而不辍,未来可期"。------《荀子·修身》

货拉拉App云录制回放测试平台的建设上,未来还有一些可提升的方向:

  1. 迭代优化模型,提升精准度和性能;
  2. 补全数据的录制回放,增加本地配置和缓存的控制;
  3. 探索使用AI大模型的识图能力,辨别APP页面上的UI异常;
  4. 和客户端精准测试结合,推荐未覆盖场景和变更相关用例;
相关推荐
我感觉。1 小时前
【机器学习chp4】特征工程
人工智能·机器学习·主成分分析·特征工程
DieYoung_Alive1 小时前
一篇文章了解机器学习(下)
人工智能·机器学习
幻风_huanfeng1 小时前
人工智能之数学基础:线性代数在人工智能中的地位
人工智能·深度学习·神经网络·线性代数·机器学习·自然语言处理
请你喝好果汁6412 小时前
单细胞|M3-4. 细胞聚类与轨迹推断
机器学习·数据挖掘·聚类
Chef_Chen4 小时前
从0开始学习机器学习--Day33--机器学习阶段总结
人工智能·学习·机器学习
databook4 小时前
『玩转Streamlit』--布局与容器组件
python·机器学习·数据分析
肖永威4 小时前
CentOS环境上离线安装python3及相关包
linux·运维·机器学习·centos
IT古董7 小时前
【人工智能】Python在机器学习与人工智能中的应用
开发语言·人工智能·python·机器学习
机器人虎哥8 小时前
【8210A-TX2】Ubuntu18.04 + ROS_ Melodic + TM-16多线激光 雷达评测
人工智能·机器学习
罗小罗同学10 小时前
医工交叉入门书籍分享:Transformer模型在机器学习领域的应用|个人观点·24-11-22
深度学习·机器学习·transformer