让标准 Maestro YAML 跑在鸿蒙上:Maestro HarmonyOS 的适配实现
让标准 Maestro YAML 跑在鸿蒙上:Maestro HarmonyOS 的适配实现
应用改完一个页面,回归时常要重新走一遍相同的操作:进入页面、输入内容、点击按钮、检查弹窗,再返回列表。单次操作不复杂,难的是每次都检查到相同的位置,出现问题时还能说清楚是哪一步出了错。
Maestro 用 YAML 描述这类界面操作。Maestro HarmonyOS 则在它的执行架构中补上鸿蒙驱动,让同一种 Flow 写法可以用于鸿蒙真机。
本文从 Maestro 的执行原理讲起,再介绍鸿蒙适配、实际用例和运行记录。对应版本为 Maestro HarmonyOS v0.1.2,记录更新于 2026 年 11 月 6 日。
开源仓库:HarmonyPandaWorkshop/maestro-harmonyos。
Maestro 是什么
Maestro 是一个开源的界面端到端测试框架,上游支持 Android、iOS 和 Web。它把一段用户操作写成 Flow,通过 CLI 执行。测试主要观察页面上能找到的控件及其状态,调用平台输入接口完成交互。上游仓库和工作原理说明介绍了这套设计。
下面是一份说明语法的表单用例。包名和控件 ID 都是示意值,接入实际应用时需要替换。
appId: com.example.form
---
- launchApp
- tapOn:
id: name_input
- inputText: 张三
- tapOn:
id: submit_button
- assertVisible: 提交成功
- takeScreenshot: form-submitted
appId 指定应用,--- 后面按顺序写操作。tapOn 负责定位和点击,assertVisible 检查预期内容,takeScreenshot 保存现场。读这份文件,就能知道它要走哪条路径、最后检查什么。
流程变长后,可以把登录、页面准备等重复步骤拆成子 Flow,通过 runFlow 调用;测试数据通过参数传入。条件分支和重试也沿用 Maestro 的语法。维护时修改公共流程即可,不必在每条用例中重复调整相同的步骤。
一条 YAML 怎样变成一次点击
在本项目固定的 Maestro 源码中,执行链路可以分成三个部分。
首先,YamlCommandReader 解析 YAML,生成执行层认识的命令。随后,Orchestra 按顺序处理命令、子流程、条件与参数。需要操作设备时,再通过 Maestro 和平台 Driver 完成布局读取、点击、输入等动作。
例如 tapOn: 提交,执行时要先读取当前页面的控件层级,在其中匹配文字,取得目标节点的位置,再发出点击。下一步的 assertVisible 会读取页面状态、检查预期结果。因此,输入命令有没有成功发出,与业务页面有没有达到预期,是两次不同的检查。
这种结构也解释了平台适配的切入点:YAML 解析和流程编排可以继续使用原来的实现,设备行为通过驱动接口提供。上游也将平台无关的执行逻辑与驱动分开维护,相关约定见贡献指南。
等待是另一项容易被忽视的工作。点击后页面可能仍在动画中,输入框也可能还没有取得焦点。执行器需要按具体命令等待布局或目标条件,再继续操作。布局稳定只能说明当前采样没有变化;“保存成功”这样的业务结果,仍要通过明确的断言判断。
鸿蒙适配接在哪一层
Maestro HarmonyOS 新增了 HarmonyOSDriver,接入 CLI 的设备选择和会话管理。应用仍然是普通鸿蒙应用,不需要把这个项目作为 HAR 引进业务代码。
HarmonyOSDriver 实现 Maestro 的设备接口,HdcHarmonyOSDevice 负责把这些操作转成实际设备命令。HDC 承担连接、远端命令和文件传输;设备上的 UITest 提供布局、输入和截图能力,aa 用于应用启动与停止。
| 测试操作 | 当前设备侧实现 |
|---|---|
| 启动应用 | aa start,传入 bundle、Ability 和需要时的 module |
| 点击、长按、滑动 | uitest uiInput 对应操作 |
| 文本输入 | 定位并点击输入框后,调用 uitest uiInput text |
| 读取控件树 | uitest dumpLayout 导出 JSON,再通过 HDC 接收 |
| 截图 | uitest screenCap 生成 PNG,接收并校验后写入结果目录 |
HDC 进程退出码只是设备调用的检查项之一。例如,当前实现还检查 uiInput 的成功回执,随后由 Flow 中的断言验证页面结果。命令回执、页面断言和资源清理各有自己的记录。
详细模块和源码入口见仓库的开发设计。
ArkUI 控件怎样对应到 Maestro 选择器
平台适配中,一项关键工作是转换控件树。
UITest 导出的 JSON 包含 attributes、children 等数据。UitestLayoutParser 保留父子关系,把节点转换成 Maestro 的 TreeNode。原来的文字、ID 和状态选择器便可以继续参与匹配。
| UITest 属性 | 转换后的用途 |
|---|---|
id,缺省时使用 key | Maestro 的 resource-id,供 YAML 的 id 选择器匹配 |
text、hint | 控件文字和输入提示 |
bounds | 节点在屏幕上的物理像素边界 |
enabled、focused、selected 等 | 判断控件状态,辅助定位和断言 |
这里没有把运行时的 accessibilityId 当作稳定业务 ID。为常用控件提供稳定的标识,会比依赖页面上的第几个同名节点更容易维护。自绘区域如果没有暴露可定位节点,也不能指望布局转换自动还原出内部按钮。
下面的片段来自 HMOS 代码工坊的按钮样式用例。开始时需要让“按钮示例”详情页位于前台,样式为 Emphasized。
appId: com.huawei.hmos.sample
name: hmos-button-style-excerpt
---
- assertVisible: '^按钮示例$'
- assertVisible: '^Emphasized$'
- tapOn:
text: '^Emphasized$'
retryTapIfNoChange: false
- assertVisible: '^Textual$'
- tapOn:
text: '^Textual$'
retryTapIfNoChange: false
- assertVisible: '^Textual$'
- assertNotVisible: '^Emphasized$'
- takeScreenshot: hmos-code-lab-button-textual
这段用例检查的是样式切换结果。仓库中的完整版本还会把按钮恢复到初始样式,方便下一次运行。页面准备、业务断言和结束状态都写进 Flow,才能把单次演示变成可重复的回归用例。
真机上遇到的问题:等待超时与收尾
把点击跑通之后,长时间重复执行暴露出了另一个问题:页面操作结束了,设备会话却没有正常释放。
一次平板重复测试中,页面稳定等待接近 5 秒窗口的末尾,驱动仍发起了新的布局查询,给这次查询留下的预算只有 58 毫秒。正常布局读取大约需要 1.7~1.8 秒,这个预算显然不足。查询超时后,临时文件删除成功,旧逻辑继续执行;但原远端命令的完成状态没有得到确认,最后收尾时触发了设备隔离。
修复时,把两个时间概念拆开了:等待窗口决定还要不要发起下一次采样,单次设备命令保留自己的完整超时预算。 同时,布局和截图查询一旦实际超时,就传播错误,停止继续派发页面动作。临时文件删掉了,不能据此认为之前的设备命令也已经结束。
会话层会登记已派发操作和临时资源,核对完成凭据、清理状态,再释放设备。无法确认时保留 unknown。符合条件的旧布局导出可以通过受控恢复处理,原失败记录仍然保留。修复记录说明了定位过程和回归结果。
这部分工作直接影响批量测试:如果只看最后一条页面断言,上一轮留下的问题可能一直带到下一轮。执行通过和会话正常收尾,需要一起核对。
VSCode 里怎样生成用例
项目配套的 VSCode 插件显示设备截图和控件树。使用者选中目标后,可以执行点击、输入、滑动、可见断言和截图等动作,逐步生成标准 YAML,再保存为 Flow。
插件通过 JSON Lines 与本机 harmonyos-bridge 进程通信。录制器生成一条 YAML,交给同一个解析器和 Orchestra 执行,成功后才把步骤追加到面板。保存后的文件继续由 CLI 回放。
截图和布局来自不同的设备调用,页面可能在两次调用之间变化。因此,录制器会检查布局稳定性,并在动作执行前核对快照和选择器。点选截图时,还要把面板坐标换算到设备的物理像素坐标。
当前记录的是插件发出的动作,不监听用户直接在手机上的触摸。对于从零编写一条用例,这种方式已经能减少查找控件和手写选择器的工作。实际操作见插件说明。
手机与平板分别跑了什么
验证使用固定版本的 HMOS 代码工坊,完整套件串联中文输入、按钮样式、警告弹窗及回程导航。三个场景和返回检查全部完成,才算一轮通过。
| 设备批次 | 完整套件首次通过 | 20 轮耗时 | 单轮平均耗时 |
|---|---|---|---|
| 手机固定版本 | 20/20,100% | 4 小时 17 分 34 秒 | 约 12 分 52 秒 |
| 平板 v0.1.2 CLI | 20/20,100% | 约 3 小时 29 分钟 | 约 10 分 25 秒 |
两批都没有使用 YAML 重试,没有运行中的人工干预,20 轮均正常清理并释放设备。耗时包括启动、执行和收尾。手机与平板保留各自的固定 CLI 和环境,因此这张表用于说明各批验证成本,不用于比较两种设备的性能。
手机记录见完整套件 20 轮,平板记录见v0.1.2 平板验证。这些批次关闭了视频录屏;分段录屏有独立验证记录。
回归脚本一旦固定,后续运行就由 CLI 执行。团队可以在版本发布前重复跑同一套用例,或者把执行入口接入 CI,保存原生 JUnit、截图和结构化结果,再根据退出码和断言结果决定是否继续后续工作。
从官方示例开始运行
第一次使用可以下载 v0.1.2 便携包。包内包含 CLI、源码、脚本和 Flow;完整 JDK 17、Node.js 22.12+、PowerShell 7、Git 和 HDC 需要在电脑上准备好。
在解压后的 source 目录运行:
node examples/hmos-code-lab/prepare.mjs
脚本会获取固定版本的官方工程。随后用 DevEco 打开生成的 examples/hmos-code-lab/app,选择 phone 配置,为自己的设备签名并安装。手机和平板均使用这个模块,完成首次引导后,将应用保持在前台。示例有前台常亮代码;界面使用简体中文,设备需保持解锁和 USB 调试授权。
仍在 source 目录执行下面的命令,把工具路径和 YOUR_DEVICE_ID 换成本机值:
$env:JAVA_HOME = 'C:/tools/jdk-17'
$env:MAESTRO_HARMONYOS_HDC = 'C:/tools/hdc.exe'
$env:MAESTRO_HARMONYOS_RECORDING = 'off'
./scripts/run-harmonyos.ps1 -CliDir ../cli `
-FlowPath ./examples/hmos-code-lab/flows/suite `
-BundleId com.huawei.hmos.sample `
-AbilityName EntryAbility -Module phone `
-DeviceId YOUR_DEVICE_ID -RunTimeoutSeconds 1320
这条命令执行一轮完整套件。准备应用的具体 SDK 要求和获取方式,见官方示例说明。使用其他应用时,先替换包名、Ability、页面准备和断言,跑通一轮,再安排重复测试。
仓库里有什么,下一步可以怎样用
仓库采用固定 Maestro 上游提交加版本化补丁的方式维护适配。upstream.lock.json 记录基线,patches/maestro 保存驱动及必要的上游修改,scripts 提供运行和验证入口,packages/vscode 保存插件源码,examples 提供用例与官方示例获取脚本。贡献代码前,可以先看开发设计和贡献说明。
当前交付入口面向 Windows,本机 HDC 连接设备;每个 CLI 会话使用一台设备。应用构建、签名和安装由开发者完成。已经验证的是 HarmonyOS 真机,独立 OpenHarmony 设备还没有实测;接口对照和系统差异记录在兼容范围中。
如果团队希望让智能体协助设计用例、编写 YAML,再提交执行任务,可以在上层接入鸿测 Agent。底层 Flow 仍然可以直接用 CLI 运行,同一份用例既能用于开发阶段验证,也能进入后续回归流程。
项目采用 Apache-2.0 许可证,是独立社区适配。源码、发行包、使用文档和验证记录都在 Maestro HarmonyOS 开源仓库。
更多推荐

所有评论(0)