SoloPi 架构解析介绍
SoloPi,源自于支付宝开源,面向移动端实现的一套无线化、非侵入、免 Root 的 Android 专项工具,能够脱离 PC 环境,在移动端实现自动化的功能、性能及兼容性测试,降低广大测试开发者的测试成本,提升测试效率。
通过本文我们将开启《SoloPi 架构解析》连载,围绕 SoloPi 在“录制回放”、“性能测试”、“一机多控”等方面的实践展开,从而深度分析在相应能力的构建与优化过程中,SoloPi 沉淀了怎样的设计思路与实战经验。
- 本文作者:
茅舍,来自支付宝,SoloPi 核心作者之一 - 本文概览:
SoloPi 录制回放 - 录制操作
SoloPi 录制回放 - 用户操作监控与控件获取
SoloPi 录制回放 - 更多控件介绍&功能分类
SoloPi 录制回放 - 用例回放
录制回放是什么?
关于录制回放,大家如果对具体功能还不是非常清晰,可以先阅览下面这个视频:
视频完整呈现了 SoloPi 录制回放的全过程,覆盖“用例录制”、“用例编辑”以及“用例回放”。因此,通过 SoloPi,开发者有能力在完全不插线的情况下,实现自动化用例的录制,并基于多种设备完成回放动作。
那么,SoloPi 是如何做到基于移动端进行自动化用例的录制和回放?毕竟自动化框架的核心在于“控件获取”和“事件驱动”,这两点往往依赖 PC 完成驱动;而在手机端,由于系统的权限管控,往往很难实现类似的功能,因此我们很少见到类似的移动端自动化测试工具。
接下来,我们将通过录制回放通用模式下的一个具体事例,来为大家展开分析 SoloPi 录制回放的具体原理。
SoloPi 录制回放深度解析
首先进入录制回放首页,点击右上角可以切换需要测试的应用。
这里我们以 Snapseed 作为待测应用(SnapSeed 是一个图像处理工具)。
点击开始执行,SoloPi 会自动跳转到对应应用,并显示一个悬浮窗。点击悬浮窗绿色三角,即可开始用例录制。
录制操作
在用例录制状态下,当你点击屏幕任意一个位置,SoloPi 会监控到具体的点击位置,并将这个位置的控件进行高亮。
如图所示,黄点表示用户点击的坐标,红框表示 SoloPi 获取的待操作控件。SoloPi 通过获取到用户点击时的坐标,以及当前页面的控件结构,来实现这种功能。
用户操作监控与控件获取
关于用于操作监控与控件获取,熟悉 Android 系统的开发者可能首先会想到 AccessibilityService。
AccessibilityService 能够获取用户的操作事件并关联对应的控件,确实可以实现大部分功能。但 AccessibilityService 也有不足的地方,比如面向 HTML5 场景,辅助功能获取控件会出现延迟从而导致获取的控件结构不够精确;另外,对于部分自定义控件,辅助功能只能获取到对应的控件,但缺乏具体的坐标信息,从而导致控件操作不符合预期。
为了应对以上不足,SoloPi 采取了“结合 Android 系统 getevnet 功能”策略,通过直接解析系统原生事件,获取到用户的操作行为。
关于 getevent 的具体内容,可以看一下 Google 提供的相关文档:
https://source.android.com/devices/input/getevent
简单来说,通过 getevent,SoloPi 可以获取到用户操作屏幕的具体坐标,以及手指落下、抬起的具体时间。
不过,getevent 命令的使用需要 SHELL 权限,这限制了普通 Android 应用获取相关数据的能力。SoloPi 通过Android 系统的无线调试功能实现了一套纯端的 SHELL 执行能力,能够在 Android 系统上执行 adb 相关命令,这块儿的具体介绍可以参考《SoloPi:支付宝开源的 Android 专项测试工具》。
通过 getevent 命令,SoloPi 获取到了用户点击的具体坐标。结合 AccessibilityService 的控件结构树,我们便可以查找到用户实际想要操作的控件了。
或许有人会问,我们既然能够获取用户的操作,以及屏幕上的控件信息,为什么录制时要卡一道,选择对应的操作,直接让用户进行操作不好吗?确实,直接操作的体验会更加顺滑,但 SoloPi 通过选择功能的界面,既可以给用户一个二次确认的机会,防止进行误操作,又能够提供更多的功能。此外,录制模式与回放模式走的是同一套执行机制,可以更好的保证用例回放的成功率。
更多控件介绍&功能分类
- 关于更多控件
通过辅助功能,可以获取到比较完整的 Native 控件结构,但对于最近较为火热的 HTML5、小程序的控件结构,就显得较为力不从心了。
针对 HTML5、小程序,SoloPi 通过 ChromeDevtoolProtocal 与 Chrome 内核通信从而获取到页面结构。这种方式同样基于 SoloPi 的 SHELL 能力,与可调试的 Chrome 内核完成通信,实现了 PC 上的 Chrome Driver 功能。通过注入 JS 获取到 HTML5 页面的完整控件结构信息。同时,SoloPi 能够将 Native 结构的控件信息与 HTML5 结构的控件信息进行统一,从而构建出一个既包含 Native 结构,也包含 HTML5 结构的页面结构树,让用户摆脱来回切换控件结构的困扰。
此外,在一些通过图像引擎渲染的场景,不管是 Native 模式,还是小程序、HTML5 模式,以上解决思路都无法正常执行。因此,SoloPi 提供了图像查找模式,根据图像的特征来进行查找,补足在游戏、AR 方面的自动化能力。
SoloPi 主要通过AbstractProvider
和AbstractNodeProcessor
这两个类来获取控件信息:通过AbstractProvider
获取控件的原始数据,并生成原始树结构,而 AbstractNodeProcessor
则着眼于处理单个控件节点信息,实现控件树的完善与结构扩展。有兴趣的读者也可以尝试实现这两个类,定义自己的控件结构。
- 关于功能分类
选择控件后,SoloPi 会弹出具体的操作选择页面,包含点击操作、输入操作、滑动操作、逻辑操作与扩展操作五大类。以“点击操作”为例,SoloPi 提供了五种功能,前两种功能(点击及长按)不用赘述,第三种为发现则点击,意味着如果发现了特定控件,则进行点击,主要用于处理弹窗和部分非必然出现的场景,快速点击用于处理一些容易消失的控件,比如播放器的控制器。此外,重复点击意味着针对某一个特定位置完成重复性的点击动作。
当开发者选择完相应操作后,SoloPi 便启动自动执行。面对部分耗时较长的操作,SoloPi 的图标将变成红色,待图标变回黄色时,意味着操作已执行完毕。以上,我们便完成了一步操作的录制。
而对于部分非控件类型的操作,如滑动屏幕,返回,Sleep 等不针对特定控件的操作,需要点击屏幕右侧的 SoloPi 图标(如下图所示),从而唤起全局操作框(注意,全局操作模式下屏幕不会显示红框)。在全局操作框下,开发者选择对应的操作完成执行动作,而操作类型主要分为“常用操作”、“应用操作”、“滑动操作”、“设备操作”、“扩展功能”与“流程管控”六大类。
用例回放
录制完成后,就可以在最近录制与查看全部用例中看到完成好的录制用例。点击用例,会跳转到测试的应用,并显示悬浮窗,点击绿色三角即可开始回放。
回放时,SoloPi 会先高亮查找到的控件,然后再执行操作。
整套 SoloPi 的录制回放,基于同一套逻辑,包括控件结构构建、控件定位、事件驱动三个部分。两者的区别主要在于控件定位:
- 在录制过程中,SoloPi 会基于用户的点击行为进行定位;
- 在用例回放时,SoloPi 会根据录制时记录的控件信息,通过 SoloPi 的控件查找算法进行定位。
与以往自动化框架通过指定文字、ID 进行查找不同,SoloPi 的控件查找算法会综合控件的各类信息,包括文字、描述、resourceId、Xpath、图像信息,对控件进行打分,并通过录制时的位置信息进行辅助定位,确定目标控件,从而实现较为精确地回放查找。
数据输出
SoloPi 的用例是以 JSON 操作序列的形式保存,每一步操作都被分为操作控件与操作方法两部分,操作控件部分包含了原始控件的各类信息,包括文字、图像、resourceId、Xpath、坐标等信息,而操作方法则包含具体的操作类型、方法参数这些信息。
除了在 SoloPi 应用间传递, SoloPi 用例也支持转化为 Appium 、 Macaca 的用例格式,可以在各种运行环境进行回放。
{
"operationId": "dt24chb7ar",
"operationIndex": 0,
"operationMethod": { // 操作方法
"actionEnum": "CLICK", // 操作方法
"encrypt": false, // 是否加密
"operationParam": {
"localClickPos": "0.7982456,0.5479452" // 相对于控件的位置
}
},
"operationNode": { // 操作节点
"assistantNodes": [ // 辅助定位节点
{
"className": "android.widget.ImageView",
"parentHeight": 1,
"resourceId": "tv.danmaku.bili:id/tab_icon"
},
{
"className": "android.widget.TextView",
"parentHeight": 1,
"resourceId": "tv.danmaku.bili:id/tab_text",
"text": "会员购"
}
],
"className": "android.widget.LinearLayout",
"depth": 12,
"extra": {
"captureImage": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEB......", // 截图
"originSize": "720,1480", // 截图尺寸
"screenSize": "1440,2960" // 原始屏幕大小
},
"id": "-4294967206",
"nodeBound": { // 控件坐标
"bottom": 2878,
"empty": false,
"left": 1203,
"right": 1317,
"top": 2732
},
"nodeType": "AccessibilityNodeTree",
"packageName": "tv.danmaku.bili",
"xpath": "/android.widget.FrameLayout/android.widget.LinearLayout[1]/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.support.v4.widget.DrawerLayout/android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[2]/android.widget.LinearLayout[1]/android.widget.FrameLayout[4]/android.widget.LinearLayout"
}
}
小结
通过本文,我们针对 SoloPi 在 Android 端录制回放的全流程及具体实现原理展开解析,后续我们将围绕“一机多控”、“性能测试”、“用例的高级玩法”展开分享。
- 目前 SoloPi 已正式开源,欢迎 Star 我们:
https://github.com/alipay/SoloPi - 在 SoloPi 体验过程中,遇到任何问题,欢迎提交 Issue:
https://github.com/alipay/SoloPi/issues
希望大家能够尝试体验 SoloPi,在过程中遇到任何疑惑,或者痛点,请积极反馈建议。期待和大家能够持续共建 SoloPi,拓展并完善更多能力。
也可以通过钉钉扫码以下二维码,进入“SoloPi 钉钉交流群”,期待你的加入。