让标准 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 引进业务代码。

标准 Maestro YAML

YamlCommandReader

Orchestra / Maestro

HarmonyOSDriver

HdcHarmonyOSDevice

HDC

鸿蒙设备:UITest / aa

ArkUI 应用页面

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,缺省时使用 keyMaestro 的 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 CLI20/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 开源仓库。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐