https://zhuanlan.zhihu.com/p/83373208
背景
由于各种新型号的移动终端层出不穷,为满足测试需要,公司投入测试的移动设备数量增长迅速,如何把散落在公司各个角落的移动设备有效的管理起来,提高资源利用率,如何快速适配各种品牌型号的终端设备,提升测试效率,如何能更快捷的接入自动化测试等,这些问题促就了知乎移动云测平台的诞生。
系统设计的主要目标在于提升知乎 App 的稳定性、优化用户体验、有效提升研发测试效率,主要功能如下:
- 设备管理:支持设备的自动接入,处理设备的使用申请。
- 远程调试:通过 web 页面在线进行单机控制或多机同步控制,提供设备屏幕实时画面展示和系统日志的展示。
- 自动化测试:支持兼容性自动化测试(Monkey)和 UI 功能自动化测试(Appium),提供自动化测试报告。
- 兼容性测试:安装、覆盖安装、启动、智能探索等 App 测试,观察不同设备下 App 的稳定性和容错性。
- 定向稳定性测试:在兼容性测试的基础上长时间循环执行定向智能探索脚本等对 App 进行测试,观察不同设备下 App 的稳定性。
架构
知乎云测平台从结构上主要分为 web 前端、后端 server 和 agent client 三个部分:其中 web 前端专注于用户交互和数据展示;后端 server 主要负责业务控制处理,包括设备注册及状态维护,指令下发,数据收集等功能;agent client 用于管理所搭载移动终端设备,包括设备信息上报,指令执行,测试报告数据回传等功能。具体实现如下:
- web 前端
基于 React 实现,在显示设计上采用阿里的 ICE 集成方案,ICE 一套基于 React 的中后台应用解决方案,它提供了大量的可复用物料,包含了一条从设计端到开发端的完整链路,配套桌面工具能够极速构建中后台应用,即使在没有 UI 设计支持的情况下, ICE 集成的内容也基本能满足设计需求,因此我们选用了 ICE。 - 后端 server
基于 Spring Boot 框架进行开发,选用 Gradle 进行项目构建 ,集成了 Spring Data JPA、Spring Security OAuth 等组件,数据库选用 MySQL;为提升数据传输效率,我们 在 Spring Boot 中整合了 Netty 用于和 agent client 进行 Socket 通信,并选用了 Protobuf 做为数据交换格式;在「设备远程调试」功能的前后端交互设计上,我们选用了 WebSocket 协议,通过后端数据推送的方式,以保证设备操作的实时性,相较于需要使用推送实时数据到客户端甚至通过维护两个 HTTP 连接来模拟全双工连接的旧的轮询或长轮询(Comet)来说,使用 WebSocket 可以极大的减少不必要的网络流量与延迟。 - agent client
使用 Java 进行开发,最终以 jar 包的形式部署在 PC 机上,分别通过 adb 和 libimobiledevice 管理 PC 上挂载的 Android 设备 和 IOS 设备,设备挂载方式支持 USB 或 WiFi;在设备远程调试功能实现上,针对 Android 设备和 IOS 设备分别采用了不同的解决方案,我们使用了 STF(Smartphone Test Farm) 提供的开源工具 Minicap 和 Minitouch,用于 Android 设备的屏幕截图和指令运行,使用 STF 的 iOS Minicap 和 Facebook 的 WebDriverAgent 框架,用于 iOS 设备的屏幕截图和指令运行,屏幕截图即作为设备远程监控页面渲染的图片流来源;UI 功能自动化测试方案采用 Appium 开源工具进行实现,稳定性自动化测试方案其中 Android 设备采用自带的命令行工具 Monkey 实现,iOS 设备则通过程序随机生成指令进行测试。
功能介绍
- 设备管理
移动设备以 USB 的接连方式挂载在部署有 agent client 的 PC 上,设备的机序列号、系统类型、系统版本、品牌、型号、 屏幕分辨率等信息会通过 agent 上报给后端 server,设备序列号作为设备唯一标识,进行设备信息的注册或更新操作。设备信息注册成功后,server 端负责维护设备在线状态,当设备离线时,由 agent 端进行状态上报给 server 端,server 端将对应设备状态修改为离线。前端页面上可以查看当前接入的所有手机设备信息及在线状态。
- 设备租借
对公司测试机进行统一登记管理,提供测试机租借服务,用户可通过平台的「设备租借」页面,查找需要的设备,并进行发起租借操作,待设备持有人同意租借后,即可领取测试机设备。
- 设备远程调试
设备远程调试功能支持通过前端页面进行远程控制设备的应用安装、启动、卸载以及常规的点击、长按、滑动等操作。
前端页面上提供一个设备屏幕等比例缩放的图片显示区域,使用 <canvas> 标签动态渲染从后端接收到的设备屏幕截图,以此实现设备屏幕局的远程监控功能。通过捕捉 <canvas> 的 onmousedown、onmousemove、onmouseover、onmouseout、onmouseup 等事件,并根据 <canvas> 控件的长高和光标当前坐标换算出触点在设备屏幕中的相对位置,最终在 agent 上根本系统平台类型生成相应的操作指令,控制设备执行指令。
同时选中多个设备,操作首个设备,其他被选中的设备会同步执行当前操作,以达到多机同步控制的效果。
- 自动化测试任务创建
支持 Monkey 和 Appium 的自动化测试,通过前端页面新建测试任务,配置相应的任务参数,任务创建成功后,由 server 端负责将任务下发给对应设备当前所挂载的 agent client,agent 收到任务即控制对应设备进行测试操作,并记录测试任务结果数据最终返回给 server 端。
- 任务报告展示
后台 server 会对接入的每个自动化测试任务的结果做统计汇总,并按照设备维度对任务的异常统计,截图,日志等信息进行展示,以快速定位测试中遇到的问题。
远程控制界面画质处理
由于需要实时操作处理,需要实时查看远程设备的显示并交互操作指令,指令数据是以 Json 的形式进行交互,只占用很少的带宽,而实时画面显示则需要将设备的实时画面以图片流的方式渲染到对应的 web 页面上,那么在使用时图片的大小、网络传输速度、渲染性能等都会影响使用体验,其中可以主动优化的就是图片大小,由于手动编译的 stf 提供的 minicap,它本身也提供了图片压缩功能,如下是测试数据:
另外设备上图片的显示内容也会影响图片的大小,像图中这种留空比较多的图片也不会特别大。实际使用中需要考虑网络占用上限以及使用体验,调研发现设备实际使用过程中用户比较关注的一般都是截图和录屏,而平台单独提供了高清的截图和单独的录屏功能,所以对实时显示的画质没有特殊要求,选取 5% 的压缩比例在公司内网 2~3 M/S 的网络速度下比较流畅,另外对流畅度优化还可以体现在图片传输帧率上,可以按照一定的比例去掉中间的部分图片,用户使用过程中会感觉到图片跳动渲染,类似 gif 图片展示,由于已经选取了 5% 的压缩比例,所以没有做此优化。
远程控制多机同步
在单机远程控制上添加了多机功能,如图中选取设备:
操作第一个设备时,指令同时分发到其他的设备上执行,如图展示:
其他的设备界面也可以同时进行操作,这个地方处理主要通过 websocket 交互传递数据,在多机界面展示图片流做了降帧处理,同时限制 10~20 台的设备使用数量,在架构上用户操作的指令主要由服务端对 agent 进行复制分发,过程如图:
知乎移动端云测试平台实践(二)—— Agent 设计和实现
背景
在云测试平台设计中,Agent 的设计一般来讲需要考虑如下一些场景:
- 移动设备的交互控制是否需要 PC 设备的支持
和移动端设备进行数据交互,主要有两种方式,一是通过常规官方建议的 PC - Mobile 的调试方式,使用公共协议如:adb、usbmuxd 进行数据交互,二是直接在 Mobile 中植入代码,通过代码调用系统 API 和服务端进行交互,省去 PC 的中转环节
- 移动设备在测试任务执行上如何隔离
设备在使用过程中还需要考虑到它会执行哪个 APP 、哪一种类型的 APP、哪一组人员、哪一种测试类型等的业务测试,所以在设计时需要考虑单个设备的「原子性」
- 设备如何做到动态运维和自动化程度
设备的维护对于服务端来讲一般都会以设备池的方式运行,这样就需要考虑到增加、删除设备对设备池带来的影响,同时如果设备的维护在公共实验室或者在远端的机房,那对应的操作就需要远程完成,所以设备的维护、重置、监控等相关的操作也需要提供远程 API
- 自动化框架选择和运行环境
自动化测试是云测试的主要目的,自动化框架的选择会影响自动化测试能力的扩展范围,所以需要选择一款合适的「基础」自动化测试框架
系统选型
知乎云测平台采用利用 PC 作为维护移动设备的服务器,交互模式为服务端 - PC 端 - 移动设备交互的方式,PC 端部署与服务端进行交互的 agent 代码,采用 socket 进行通信确保即时性,同时通过 http 主动消费服务端维护的任务池,执行完毕之后返回数据并循环进行,在自动化任务执行上采用 Appium 做为「基础」自动化测试框架。系统选型主要基于如下几点:
- 大部分自动化框架的运行都基于 Mobile 挂载 USB 对应的 PC 上,同时 Agent 服务、自动化框架、硬件运维、异常处理等都需要稳定的 PC 运行环境
- Netty Socket 框架可以提供稳定的即时数据交互
- 任务数据的处理在精度上要求更高,所以采用 Http 请求任务池的方式
- 自动化框架选取在各方面都具有一定优势的 Appium,「主要是社区比较活跃」,同时设备执行测试任务的隔离也主要由 Appium 来进行控制,如下是对比图:
Agent 模块组成
Agent 模块在云测试平台中主要负责设备控制、维护的功能,主要包括三大模块如下:
实时任务
基于 Netty Socket
实时交互如下图展示:
移动端设备控制
在这里移动端设备控制是方便使用者可以在远端通过一定的方式远程控制设备的操作,同时看到设备的实时显示画面,经过各种调研、测试、实验选取 openstf [https://github.com/openstf/stf] 开源的两款工具 minicap / minitouch 来完成这部分功能。stf 是一款可以远程调试移动手机、手表、Pad 的 web 工具,使用效果如图:
参考 stf 的 nodejs 源码中 minicap / minitouch 这两款工具的使用方式,由于 Agent 平台使用 java / kotlin 编写,而 minicap / minitouch 是用 c/c++ 编写,所以采用将手动编译完成的 minicap / minitouch 运行程序内置到 Agent 中提供调用。
minicap
github 地址: https://github.com/openstf/minicap
当设备接入时 minicap 初始化线程会分为如下几个步骤对设备初始化和启动:
minitouch
github 地址: https://github.com/openstf/minitouch
当设备接入时 minitouch 初始化线程也会分为如下几个步骤对设备进行初始化:
Agent minitouch / minicap 和 pc 交互图如下:
定时任务
定时任务,主要是自动化测试任务的数据交互,云测试平台的优势在于可以动态的调用多个设备进行测试,而每个设备的运行又是独立的,所以任务的运行设计最小粒度以单个设备运行为准,那么无论是兼容性测试、安装测试、自动化脚本测试、性能测试等都会在服务端拆为单个的任务,以任务池的方式存放到多维队列中,Agent 只需要在交互时拿到对应设备的队列任务运行即可。与设备远程控制要求交互的即时性不一样,远程控制要求指令必须在毫秒级以内完成数据的下发和返回,自动化任务交互可以容忍一定的延迟,所以在与服务端交互上采用了 Http 轮询方式:
Http 轮询
轮询的详细过程如下图示:
设备维护
Agent 部署完成之后,会主动监听当前 adb / usb 的设备连接状态,当有新的设备接入、已有设备移除时会执行图中的相应步骤来维护设备的连接状态,做到移动设备的自动化接入和移除。
同时在设备的任务执行、远程控制、离线上线等过程中都会和服务端进行数据维护交互,同步设备的状态到服务端,为服务端的任务处理、排队策略、动态任务分配等提供判断依据
遇到的问题和处理
1. minicap / minitouch 设备兼容问题?
由于后面设备类型的未知性,只是在 Agent 内部对指定的机型设备做了特殊兼容处理,除了几款 stf 天然不支持的设备类型,并未发现有大量的兼容问题。
2. 设备图片传输实时性问题?
目前大部分任务处理都是在公司内网环境,目前只是做了图片数据级的压缩,可以预见在网络环境较差的情况下,可能需要图片像素级的压缩处理。
图片处理是远端控制的重要环节,主要处理方式如下:
我们从目前比较主流移动设备拿到的单个图片的大小应该在 1M 左右,如果是高分辨率的设备或者 iPad、AndroidPad 等设备可能会更大,做纯数据压缩最多只能做到百 KB 大小的级别。
在上面的前提下,我们可以牺牲一部分图片的清晰度来换取操作的流畅度和网络性能,如图所示我们先将原图按照比例等比压缩到一定大小,然后将压缩后的图片在页面上渲染到和设备同样大小,
这样丢失的清晰度和压缩的比例有直接关系,在数据传输的过程中可以根据网络质量的好坏动态修改图片压缩的比例。
经过图片压缩和数据压缩,图片的传输量级可以缩小到十 KB 大小的级别,经过调整大部分设备图片数据的大小传递都在 20 KB 上下浮动,并且同时具有较好的清晰度。
3. 设备维护线程、自动化处理线程稳定性问题?
针对线程运行、本地环境不稳定,添加了一些页面手动控制操作,当 Agent 运行过程中发现设备假死、卡顿、超时等问题会直接通过 IM 发送报警,运维可以通过页面操作重新初始化设备相关线程及时发现和处理问题。
知乎移动端云测试平台实践(三)—— 自动化测试方案设计和实现
背景
为了充分利用云测试平台维护的设备,提升空闲设备使用率,开展自动化测试替代部分回归测试、重复性测试和多设备兼容测试,同时满足如下几种类型的自动化测试需求:
随机测试(monkey、随机操作指令):
在多设备执行的基础上完成安装、启动、覆盖安装、monkey 测试 / 随机指令、卸载等一系列操作。
遍历测试(深度遍历、智能探索):
在 monkey 测试 / 随机指令的基础上,对执行步骤进行改造,将随机操作替换为 app 内部页面的深度遍历,设计算法策略在一定程度上对被测 app 进行智能探索操作。
UI 自动化测试(appium):
在自动化测试框架支持的基础上,提供多设备脚本运行能力,测试开发人员只需要编写符合规则的测试脚本即可以在云测试平台上选择多个设备进行测试,获得测试结果。
在以上自动化测试的同时,测试报告中需要体现如下内容:
- 实时生成测试报告、测试结果数据
- 实时获取设备的日志并有对应的分析结果
- 可控的自动化测试过程
- 执行过程中设备页面截图或录制视频
- 测试结果中设备类型数据分析
在上面背景说明中,介绍了几种测试类型需求,前两种的设计流程比较简单,也不需要外部人员的参与,第三种测试类型设计比较复杂,后续文章说明也以第三种测试类型为主。
自动化执行框架设计
自动化框架
在
中自动化框架调研对比和各大云测试平台的使用,选择了在各方面都具有一定优势的自动化测试框架 appium 作为自动化测试的执行控制层,本地启动 appium hub 的方式接收脚本执行请求,这里就不多加赘述了。
脚本语言和执行框架
云测试平台是由 Java + kotlin 开发,客户端控制都是基于 Java 实现,这里自然选择 Java 作为脚本语言,后续的脚本、流程说明也是以 Java 语言实现为主,但是在脚本语言选择上这里不是强制要求,同样可以选择Ruby、Python、PHP,、JavaScript 和 C#,只是后续的实现需要平台多做一步兼容而已。
在脚本执行方面没有使用类似 junit、testng等第三方的运行框架,主要是为了保持在运行过程中对脚本运行的控制和运行数据的交互,如下是脚本运行实现方案:
1.由平台提供一定限制范围的脚本编写能力
主要是指运行过程的脚本编写,以及如何提供类似截图、步骤日志、检查点等公用方法,对于 Java 来说可以将一些公共的方法抽象出来放到脚本的父对象中,通过继承将脚本编写能力赋予给脚本本身,Python 也可以统一一个标准的类库,通过引入的方式使用。
2.运行时由 agent 动态编译编写完成的脚本,反射实例化脚本对象
运行时处理脚本需要区分动态语言和非动态语言,还是以 Java、Python 为例,由于没有借用第三方的测试框架,触发脚本运行对于 Java 来说需要进行编译,也就是标题中说到的动态编译,然后通过反射实例化对象运行,这里有两个要求,首先脚本编写需要在云测试平台限定的包内,其次脚本运行、继承的方法需要符合约定的规则。对于 Python 来说先将脚本内容以 IO 的方式写到内存中,然后反射通过字符串的形式,导入模块、去模块寻找约定函数执行。
3.使用反射实例对象运行脚本,并调用实例中的方法和脚本进行数据、强控制交互
实例化脚本后开始运行脚本,运行前需要将所需要的运行资料注入到实例中,例如: appium 的 appiumDriver,运行同时可以随时调用实例化对象中的约定方法对脚本运行进行控制,比如获取执行步骤、日志、图片,传递参数,控制脚本暂停、运行、停止等交互,这也是为什么没有使用一些第三方框架来触发测试的原因。
通过上述过程基本上实现了用例到执行的基本过程,如图:
也就是说只要提供了符合规范的脚本,就可以利用框架的通用性将脚本运行在任何 appium 支持的设备上,在架构上也剥离了自动化测试最关键的工作部分即:脚本编写,也为脚本管理、数据模板结合等用例的功能丰富提供更多可能性。
流程设计
如下是脚本自动化测试完整的业务执行方案:
自动化测试 UI 代码以 git 工程的方式托管在公司的 git 服务器上,在工程基础上编写脚本,调试脚本通过之后合并入 git 工程,在 gitlab 上每一个脚本都会有一个唯一的地址,通过这个地址可以获取到指定脚本,然后通过接口 / 表单的方式提交脚本运行测试,运行完成得到质量报告。
值得说明一下的是,在自动化测试过程中,公共方法、selenium / appium 公共 page object 等脚本数据会随着自动化测试的深入越发庞大,所以需要图中的公共方法抽取过程,这样带来的好处是由于脚本的抽取、复用会使脚本的编写效率会越来越快。
如下是脚本执行流程结构图,包含脚本执行的流程和结果收集:
脚本执行数据、执行交互设计方案:
这里主要体现的是脚本和运行平台间的数据交互、执行能力交互,比如脚本执行时需要使用到 appium 的 driver,而这个 driver 是通过平台的设备参数来决定的,在运行时平台动态生成 driver 然后通过协议类的方式注入 driver 到脚本内部,运行过程中通过协议类的停止、暂停、等待、销毁等方法进行控制,运行完成后通过协议类获取到运行结果。在脚本需要一些特定的功能时,也可以通过协议类引入接口方法,然后运行平台通过接口代理的方式动态注入实现类的方式实现。
在实施的过程中发现有两个难点,主要如下:
- 类和第三方包引用管理引起的脚本编译问题
在自动化测试脚本编写过程中,不可避免需要使用一些引入包,比如一些方便的工具类使用包等,对于 JAVA 脚本来讲更新类、包引入等都需要重新编译部署才可以运行使用,测试平台不可能会因为脚本类、包的变化重新编译部署平台,而脚本的编写绝大部分都是类引入的变化,包引入变化的比例很小,所以在类的变化上使用 Java 动态编译技术解决类的动态引入,第三方的包引入更新则通过平台工程的发版提前引入这些包升级完成。 - 协议类和包更新的自动化更新过程
在云平台和脚本工程中间是通过协议类进行数据交互,而定义的这个协议类和包发生之后按照上面的方案来说是需要云平台重新部署才可以的,在实践中发现脚本的能力建设和扩展等都需要通过协议类的修改才能实现,这就决定了这个协议类会频繁的发生变更,所以 Agent 工程中在动态编译前,手动校验了服务器上的协议类版本,如果发现了新版则下载新版的协议类 jar,在动态编译时替换到 -classpath 的协议类版本参数,这样就做到了指定协议类内容的动态更新和自动化部署。
脚本设计
在上面提到过 「对于 Java 来说可以将一些公共的方法抽象出来放到脚本的父对象中,通过继承将脚本编写能力赋予给脚本本身」,所以脚本设计分两个模块:
协议父对象实现
如图所示父对象中提供了测试驱动 androidDriver、log 日志、checkPoint 检查点、img 截图等脚本能力
用例子对象编写
如图所示,实际的脚本继承父对象的能力之后,可以完成编写相关页面测试逻辑、业务逻辑的自动化测试任务
脚本调试和运行
平台脚本调试可以通过如下两种方式进行:
本地调试
自行使用 main 方法、或者使用单元测试框架调试脚本通过
然后将主体修改为规定格式的自动化脚本,视情况修改 appiumDriver 引入方式、添加日志记录、截图、检查点等
远程调试
远程调试需要在云测试平台上申请一台使用设备如图:
点击图中的按钮,将脚本粘入运行,如下图:
测试报告展示
报告主要分为如下几个部分
测试基本信息
主要展示测试过程的 app 基础信息和测试相关的一些信息展示,概况展示脚本所有检查点的通过比例,脚本运行的设备、通过、失败的数据统计,运行概况展示每一个脚本在每一个设备上运行的过程和结果,以脚本和设备为维度,log 链接中会展示详细的脚本运行步骤日志
检查点概况
脚本运行的核心结果,主要展示此次自动化测试过程中所有的测试结果
截图/步骤/异常
在脚本运行结束后,通过反射的方式获取到脚本用例的详细数据,通过 「截图/步骤/异常」细化展示每一次运行的步骤、图片、和检查结果
性能图表、过程视频
同时在脚本运行过程中也会通过 appium 框架功能实现设备的网络上下行统计、
和
,然后渲染性能数据生成图表并提供视频回放脚本执行过程