SoloPi 架构解析 | 录制回放的原理与实战

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 架构解析 | 录制回放的原理与实战

如图所示,黄点表示用户点击的坐标,红框表示 SoloPi 获取的待操作控件。SoloPi 通过获取到用户点击时的坐标,以及当前页面的控件结构,来实现这种功能。

用户操作监控与控件获取

关于用于操作监控与控件获取,熟悉 Android 系统的开发者可能首先会想到 AccessibilityService。
AccessibilityService 能够获取用户的操作事件并关联对应的控件,确实可以实现大部分功能。但 AccessibilityService 也有不足的地方,比如面向 HTML5 场景,辅助功能获取控件会出现延迟从而导致获取的控件结构不够精确;另外,对于部分自定义控件,辅助功能只能获取到对应的控件,但缺乏具体的坐标信息,从而导致控件操作不符合预期。
为了应对以上不足,SoloPi 采取了“结合 Android 系统 getevnet 功能”策略,通过直接解析系统原生事件,获取到用户的操作行为。

关于 getevent 的具体内容,可以看一下 Google 提供的相关文档:
https://source.android.com/devices/input/getevent

简单来说,通过 getevent,SoloPi 可以获取到用户操作屏幕的具体坐标,以及手指落下、抬起的具体时间。

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 结构的页面结构树,让用户摆脱来回切换控件结构的困扰。

SoloPi 架构解析 | 录制回放的原理与实战

此外,在一些通过图像引擎渲染的场景,不管是 Native 模式,还是小程序、HTML5 模式,以上解决思路都无法正常执行。因此,SoloPi 提供了图像查找模式,根据图像的特征来进行查找,补足在游戏、AR 方面的自动化能力。

SoloPi 主要通过AbstractProviderAbstractNodeProcessor这两个类来获取控件信息:通过AbstractProvider 获取控件的原始数据,并生成原始树结构,而 AbstractNodeProcessor 则着眼于处理单个控件节点信息,实现控件树的完善与结构扩展。有兴趣的读者也可以尝试实现这两个类,定义自己的控件结构。

  • 关于功能分类

选择控件后,SoloPi 会弹出具体的操作选择页面,包含点击操作输入操作滑动操作逻辑操作扩展操作五大类。以“点击操作”为例,SoloPi 提供了五种功能,前两种功能(点击及长按)不用赘述,第三种为发现则点击,意味着如果发现了特定控件,则进行点击,主要用于处理弹窗和部分非必然出现的场景,快速点击用于处理一些容易消失的控件,比如播放器的控制器。此外,重复点击意味着针对某一个特定位置完成重复性的点击动作。

SoloPi 架构解析 | 录制回放的原理与实战

当开发者选择完相应操作后,SoloPi 便启动自动执行。面对部分耗时较长的操作,SoloPi 的图标将变成红色,待图标变回黄色时,意味着操作已执行完毕。以上,我们便完成了一步操作的录制。
而对于部分非控件类型的操作,如滑动屏幕,返回,Sleep 等不针对特定控件的操作,需要点击屏幕右侧的 SoloPi 图标(如下图所示),从而唤起全局操作框(注意,全局操作模式下屏幕不会显示红框)。在全局操作框下,开发者选择对应的操作完成执行动作,而操作类型主要分为“常用操作”、“应用操作”、“滑动操作”、“设备操作”、“扩展功能”与“流程管控”六大类。

SoloPi 架构解析 | 录制回放的原理与实战

用例回放

录制完成后,就可以在最近录制查看全部用例中看到完成好的录制用例。点击用例,会跳转到测试的应用,并显示悬浮窗,点击绿色三角即可开始回放。
回放时,SoloPi 会先高亮查找到的控件,然后再执行操作。

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,在过程中遇到任何疑惑,或者痛点,请积极反馈建议。期待和大家能够持续共建 SoloPi,拓展并完善更多能力。
也可以通过钉钉扫码以下二维码,进入“SoloPi 钉钉交流群”,期待你的加入。

SoloPi 架构解析 | 录制回放的原理与实战

上一篇:Jquery上传插件Uploadify无刷新上传文件


下一篇:Lazy Load, 延迟加载图片的 jQuery 插件【备忘】