Maestro 移动 UI 自动化测试入门教程
本文带你从零开始掌握 Maestro —— 一款开源的跨平台移动 UI 自动化测试框架。涵盖安装配置、YAML 测试流编写、核心命令、选择器、高级用法及实战案例,让你 10 分钟写出第一条自动化测试。
一、Maestro 是什么?
Maestro 是一款开源的端到端移动 UI 自动化测试框架,支持Android、iOS 和 Web应用(包括 React Native、Flutter 和混合应用)。它的核心理念是"拥抱不稳定性",通过人类可读的 YAML 语法和解释型执行引擎,让测试编写变得简单高效。
与传统自动化框架(如 Appium、XCUITest)相比,Maestro 最大的优势在于极低的上手门槛—— 你不需要编写复杂的代码,只需用 YAML 描述用户操作即可。
核心特性
| 特性 | 说明 |
|---|---|
| 跨平台 | 一套 YAML 语法,同时测试 Android、iOS 和 Web 应用 |
| 人类可读的 YAML | 用launchApp、tapOn、assertVisible等命令表达交互 |
| 内置抗抖动 | 自动等待 UI 稳定,无需手动写sleep() |
| 解释型执行 | 无需编译,修改即运行,快速迭代 |
| 智能元素定位 | 默认使用 Accessibility Tree,模拟真实用户视角 |
| JavaScript 集成 | 在 YAML 中内嵌 JS 处理复杂逻辑、调用外部 API |
| 测试录制 | 自动将执行过程录制成 MP4 视频 |
| Maestro Studio | 可视化测试构建器,支持录制交互、检查元素 |
为什么选择 Maestro?
- 学习曲线低:5 分钟写出第一条测试,无需编程经验
- 维护成本低:YAML 语法直观,测试即文档
- 稳定性高:内置智能等待机制,减少 flaky tests
- 生态完善:支持本地测试、CI/CD 集成、云端测试
二、环境准备与安装
2.1 前置条件
安装 Maestro 前,请确保系统已安装Java 17 或更高版本。
验证 Java 版本:
java-version如果未安装 Java,推荐使用 Temurin JDK 或 Oracle JDK 安装。确保
JAVA_HOME环境变量指向 Java 17+ 的安装路径。
2.2 安装 Maestro CLI
macOS 安装
方式一:使用 curl 脚本安装
curl-fsSL"https://get.maestro.mobile.dev"|bash方式二:使用 Homebrew 安装
brew tap mobile-dev-inc/tap brew trust--formulamobile-dev-inc/tap/maestro brewinstallmobile-dev-inc/tap/maestromacOS 用户还需安装最新版 Xcode 和 Xcode Command Line Tools(用于 iOS 模拟器测试)。
Windows 安装
- 前往 Maestro GitHub Releases 下载最新的
maestro.zip - 解压到稳定目录(如
C:\maestro) - 将 Maestro 的
bin目录添加到系统 PATH:
setx PATH"%PATH%;C:\maestro\bin"- 重启终端使配置生效
Linux 安装
curl-fsSL"https://get.maestro.mobile.dev"|bash安装完成后,默认安装路径为$HOME/.maestro/bin。如果maestro命令不可用,手动添加 PATH:
exportPATH="$PATH:$HOME/.maestro/bin"2.3 验证安装
运行以下命令,若显示帮助信息则安装成功:
maestro--help2.4 准备测试设备
Maestro 需要一个正在运行的设备或模拟器来执行测试。
Android 测试:
- 打开 Android Studio
- 进入 Virtual Device Manager
- 启动一个虚拟设备(如 Pixel 8)
- 等待设备启动到主屏幕
iOS 测试:
- 打开 Xcode
- 启动 iOS 模拟器(如 iPhone 15)
- 确保模拟器处于运行状态
三、第一个测试:10 分钟快速上手
3.1 创建测试文件
创建一个新目录并新建contacts.yaml文件:
appId:com.google.android.contacts----launchApp:clearState:true-tapOn:"Allow"-tapOn:Create contact-tapOn:First name-inputText:John-tapOn:Last name-inputText:Doe-tapOn:Company-inputText:Maestro-tapOn:"+1"-inputText:111-111-1111-tapOn:Save-back3.2 运行测试
确保模拟器正在运行,然后执行:
maestrotestcontacts.yamlMaestro 会连接到模拟器,按顺序执行每个步骤。终端会显示实时进度报告,你可以在模拟器上看到自动化操作的过程。
3.3 测试录像
在 Flow 中加入录像命令,执行后会在当前目录生成recording.mp4:
appId:com.google.android.contacts----launchApp:clearState:true-startRecording:recording# 开始录像-tapOn:Create contact-tapOn:First name-inputText:John-tapOn:Last name-inputText:Doe-tapOn:Save-stopRecording# 停止录像,生成 recording.mp4四、Flow 结构详解
Maestro 的测试文件称为Flow,使用 YAML 格式编写。一个标准的 Flow 由两部分组成,用---分隔:
# ========== 配置区 ==========appId:com.example.app# 必填:被测应用的包名/Bundle IDname:登录测试# 选填:Flow 的自定义名称tags:# 选填:标签,用于筛选测试-smoke-test-loginenv:# 选填:环境变量USERNAME:"user@example.com"PASSWORD:"123456"---# ========== 命令区 ==========-launchApp# 启动应用-tapOn:"Username"# 点击用户名输入框-inputText:${USERNAME}# 输入环境变量-tapOn:"Password"-inputText:${PASSWORD}-tapOn:"Login"# 点击登录-assertVisible:"Welcome"# 断言欢迎信息可见配置区字段说明
| 字段 | 必填 | 说明 |
|---|---|---|
appId | 是 | 被测应用的包名(Android)或 Bundle ID(iOS) |
name | 否 | Flow 的显示名称,会出现在测试报告中 |
tags | 否 | 标签列表,配合--include-tags/--exclude-tags使用 |
env | 否 | 环境变量映射,在命令区通过${变量名}引用 |
五、核心命令速查
5.1 常用命令一览
| 命令 | 说明 | 示例 |
|---|---|---|
launchApp | 启动应用 | - launchApp |
killApp | 强制关闭应用 | - killApp |
tapOn | 点击元素 | - tapOn: "登录" |
doubleTapOn | 双击元素 | - doubleTapOn: "头像" |
longPressOn | 长按元素 | - longPressOn: "消息" |
inputText | 输入文本 | - inputText: "hello" |
eraseText | 删除文本 | - eraseText: 10 |
swipe | 滑动操作 | - swipe: { direction: UP } |
scroll | 滚动 | - scroll |
scrollUntilVisible | 滚动直到元素可见 | - scrollUntilVisible: { element: "加载更多" } |
back | 返回(Android) | - back |
pressKey | 按键 | - pressKey: Enter |
hideKeyboard | 隐藏键盘 | - hideKeyboard |
assertVisible | 断言元素可见 | - assertVisible: "欢迎" |
assertNotVisible | 断言元素不可见 | - assertNotVisible: "错误" |
takeScreenshot | 截图 | - takeScreenshot: result |
waitForAnimationToEnd | 等待动画结束 | - waitForAnimationToEnd |
openLink | 打开链接 | - openLink: "https://example.com" |
5.2 命令详细用法
launchApp —— 启动应用
# 基本启动-launchApp# 启动并清除应用状态(重新开始)-launchApp:clearState:true# 启动并清除权限设置-launchApp:clearState:trueclearKeychain:truetapOn —— 点击元素
# 通过文本点击-tapOn:"登录"# 通过 ID 点击-tapOn:id:login_button# 通过文本点击(指定第几个匹配项)-tapOn:text:"删除"index:2# 多条件组合定位-tapOn:text:"提交"enabled:truebelow:"个人信息"inputText —— 输入文本
# 直接输入-inputText:"hello world"# 输入环境变量-inputText:${USERNAME}# 输入数字-inputText:"13800138000"swipe —— 滑动操作
# 方向滑动-swipe:direction:UP# 指定百分比区域滑动-swipe:start:50%,80%end:50%,20%# 元素间滑动-swipe:from:id:item_1to:id:item_5assertVisible —— 断言元素可见
# 简单断言-assertVisible:"登录成功"# 带超时的断言-extendedWaitUntil:visible:"欢迎页面"timeout:10000# 可选断言(不通过也不会失败)-assertVisible:text:"弹窗广告"optional:true六、选择器(Selectors)
选择器是 Maestro 定位 UI 元素的核心机制。Maestro 默认使用Accessibility Tree(无障碍树),从用户视角来识别界面元素。
6.1 选择器类型
文本选择器(最常用)
# 简写形式-tapOn:Login# 完整形式-tapOn:text:Login
text默认支持正则表达式,可用于匹配动态文本。
ID 选择器(最稳定)
-tapOn:id:submit_buttonID 是 Accessibility Identifier,不受语言切换影响,适合多语言应用。
索引选择器
当有多个匹配元素时,用index指定第几个(从 0 开始):
-tapOn:text:"删除"index:1# 点击第 2 个"删除"按钮坐标选择器
-tapOn:point:50%,50%6.2 关系选择器
当元素本身没有唯一标识时,可以用相对位置来定位:
# 点击"密码"下方的元素-tapOn:below:"密码"# 点击"标题"上方的元素-tapOn:above:"标题"# 点击某个父容器内的元素-tapOn:text:"删除"childOf:id:list_item6.3 状态选择器
# 只在元素可点击时操作-tapOn:text:"提交"enabled:true# 检查勾选状态-assertVisible:text:"记住密码"checked:true# 检查焦点状态-assertVisible:text:"搜索框"focused:true6.4 选择器最佳实践
| 场景 | 推荐策略 |
|---|---|
| 有固定文本的按钮/标签 | 使用text选择器,直观且自文档化 |
| 图标、图片等无文字元素 | 使用id(Accessibility Identifier),跨语言稳定 |
| 动态文本或无唯一标识 | 用关系选择器(above/below)锚定位置 |
| 文本部分变化 | 使用正则text: "订单.*成功" |
| 等待异步加载完成 | 在选择器中加enabled: true,自动等待可交互状态 |
七、高级用法
7.1 子 Flow(Subflows)
将通用操作抽取为子 Flow,实现复用。例如创建login.yaml:
# login.yamlappId:com.example.app----tapOn:"用户名"-inputText:${USERNAME}-tapOn:"密码"-inputText:${PASSWORD}-tapOn:"登录"-assertVisible:"首页"在其他 Flow 中调用:
# main_flow.yamlappId:com.example.app----launchApp-runFlow:login.yaml# 调用子 Flow-tapOn:"我的订单"-assertVisible:"订单列表"7.2 条件执行
使用runFlow配合when条件来控制执行逻辑:
-runFlow:when:visible:"升级提示"commands:-tapOn:"稍后"7.3 循环
-repeat:times:3commands:-tapOn:"下一个"-assertVisible:"图片"7.4 JavaScript 集成
在 YAML 中直接执行 JavaScript,处理复杂逻辑:
appId:com.example.app----evalScript:${output.date = new Date().toISOString()}-tapOn:"日期"-inputText:${output.date}发起 HTTP 请求:
-evalScript:${output.response = http.get('https://api.example.com/test-data')}-inputText:${output.response.body.id}7.5 重试机制
对不稳定操作使用retry:
-retry:maxRetries:3commands:-tapOn:"刷新"-assertVisible:"数据加载完成"八、CLI 命令参考
8.1 常用命令
| 命令 | 说明 |
|---|---|
maestro test <flow.yaml> | 执行测试 |
maestro test -c <flow.yaml> | 连续模式,文件变更自动重跑 |
maestro test --include-tags=smoke . | 只运行带smoke标签的 Flow |
maestro test --format=JUNIT . | 生成 JUnit 格式报告 |
maestro start-device --platform=android | 启动 Android 模拟器 |
maestro list-devices | 列出本地可用设备 |
maestro record <flow.yaml> | 录制测试执行过程 |
maestro download-samples | 下载官方示例 |
maestro hierarchy | 打印当前应用的视图层级 |
8.2 test 命令常用选项
| 选项 | 说明 |
|---|---|
-c, --continuous | 连续模式,监控文件变化自动重跑 |
-e, --env=KEY=VALUE | 设置环境变量 |
--include-tags=tags | 只运行包含指定标签的 Flow |
--exclude-tags=tags | 排除包含指定标签的 Flow |
--format=FORMAT | 报告格式:JUNIT、HTML、NOOP |
--output=PATH | 指定报告输出路径 |
--device=UDID | 指定运行的设备 ID |
--platform=PLATFORM | 指定平台:android、ios、web |
-s, --shards=COUNT | 并行分片执行 |
8.3 实用示例
# 运行单个 Flowmaestrotestlogin.yaml# 运行目录下所有 Flowmaestrotest./flows/# 只运行冒烟测试maestrotest--include-tags=smoke ./flows/# 连续开发模式maestrotest-clogin.yaml# 生成 HTML 报告maestrotest--format=HTML--output=report.html ./flows/# 传入环境变量maestrotest-eUSERNAME=test@test.com-ePASSWORD=123456login.yaml# 在指定设备上运行maestro--device=emulator-5554testlogin.yaml# 启动设备maestro start-device--platform=android --device-os=android-34九、实战案例:登录功能测试
下面通过一个完整的登录测试场景,综合运用前面学到的知识。
9.1 测试场景
- 启动应用
- 处理首次启动的权限弹窗
- 输入用户名和密码
- 点击登录
- 验证登录成功
- 退出登录
9.2 测试文件
# login_test.yamlappId:com.example.myappname:登录功能测试tags:-smoke-loginenv:USERNAME:"testuser@example.com"PASSWORD:"Test@1234"---# 启动应用,清除状态-launchApp:clearState:true# 处理可能出现的权限弹窗-runFlow:when:visible:"允许"commands:-tapOn:"允许"# 进入登录页面-tapOn:"登录"# 输入用户名-tapOn:id:username_input-inputText:${USERNAME}# 输入密码-tapOn:id:password_input-inputText:${PASSWORD}# 点击登录按钮-tapOn:text:"登录"index:1enabled:true# 验证登录成功-assertVisible:"首页"-takeScreenshot:login_success# 退出登录-tapOn:"我的"-scroll-tapOn:"退出登录"-tapOn:"确认"-assertVisible:"登录"9.3 运行测试
# 基本运行maestrotestlogin_test.yaml# 生成 JUnit 报告(用于 CI/CD)maestrotest--format=JUNIT--output=report.xml login_test.yaml# 连续模式开发调试maestrotest-clogin_test.yaml十、Maestro Studio 可视化工具
Maestro Studio 是一个轻量级的可视化测试构建工具,帮助你快速编写测试。
启动 Maestro Studio
maestro studio核心功能
| 功能 | 说明 |
|---|---|
| 视觉流构建器 | 点击界面元素自动生成对应命令 |
| 元素检查器 | 查看元素的 ID、文本、层级等属性 |
| 实时预览 | 在模拟器上操作,实时生成 YAML |
| AI 辅助 | 用自然语言描述操作,AI 生成命令 |
对于初学者,推荐先用 Maestro Studio 录制操作生成基础 Flow,再手动优化 YAML。
十一、测试优化与最佳实践
11.1 减少测试 flaky
- 善用
enabled: true:在点击按钮前确保它可交互 - 使用
optional: true:对可能出现的弹窗做可选断言 - 避免硬等待:用
assertVisible代替sleep - 合理使用
retry:对网络相关操作加重试
# 处理可能出现的弹窗-runFlow:when:visible:"更新提示"commands:-tapOn:"稍后提醒"# 等待元素可点击再操作-tapOn:text:"提交"enabled:true11.2 测试组织结构
推荐的目录结构:
project/ ├── config.yaml # 全局配置 ├── flows/ │ ├── login/ # 按功能模块分组 │ │ ├── login_success.yaml │ │ └── login_failure.yaml │ ├── search/ │ │ └── search_flow.yaml │ └── checkout/ │ └── checkout_flow.yaml ├── subflows/ # 可复用的子 Flow │ ├── login.yaml │ └── navigate_home.yaml └── reports/ # 测试报告输出11.3 使用 config.yaml 统一配置
在项目根目录创建config.yaml,设置全局行为:
# config.yamlappId:com.example.myapp# 测试执行配置flowOrder:-subflows/login.yaml-flows/# 全局环境变量env:API_BASE_URL:"https://test-api.example.com"运行时指定配置文件:
maestrotest--config=config.yaml ./flows/11.4 CI/CD 集成
在 GitHub Actions 中集成 Maestro:
# .github/workflows/test.ymlname:Maestro Testson:[push,pull_request]jobs:test:runs-on:macOS-lateststeps:-uses:actions/checkout@v4-uses:reactivecircus/android-emulator-runner@v2with:api-level:34script:|curl -fsSL "https://get.maestro.mobile.dev" | bash export PATH="$PATH:$HOME/.maestro/bin" maestro test --format=JUNIT --output=report.xml ./flows/-uses:actions/upload-artifact@v4with:name:test-reportpath:report.xml十二、常见问题
Q1:元素找不到怎么办?
- 使用
maestro hierarchy命令查看当前界面的视图层级 - 用 Maestro Studio 的元素检查器查看元素属性
- 尝试使用不同的选择器(text、id、关系选择器)
- 检查元素是否在 WebView 或 Flutter 渲染层中
Q2:测试运行超时?
设置启动超时环境变量:
exportMAESTRO_DRIVER_STARTUP_TIMEOUT=180000Q3:如何测试 Flutter 应用?
Maestro 原生支持 Flutter。确保 Flutter 应用启用了语义信息(Semantics),然后在 Flow 中正常使用选择器即可。
Q4:如何处理系统弹窗?
使用runFlow条件执行来处理:
-runFlow:when:visible:"Allow"commands:-tapOn:"Allow"Q5:如何在多台设备上并行测试?
使用--shards选项:
maestrotest--shards=3./flows/总结
Maestro 以其简洁的 YAML 语法、内置的抗抖动机制和跨平台支持,大幅降低了移动 UI 自动化测试的门槛。本文涵盖了从安装到实战的完整流程:
- 安装配置:一行命令完成安装,Java 17+ 即可运行
- 快速上手:YAML 描述操作,
maestro test一键执行 - 核心命令:
launchApp、tapOn、inputText、assertVisible等 - 选择器:文本、ID、关系、状态等多维定位策略
- 高级用法:子 Flow 复用、条件执行、循环、JS 集成
- 工程化:标签筛选、报告生成、CI/CD 集成
对于想要快速建立移动 UI 自动化测试体系的团队,Maestro 是一个非常值得尝试的选择。建议从简单的冒烟测试开始,逐步扩展到完整的回归测试套件。
官方资源
- 官方文档:https://docs.maestro.dev
- GitHub 仓库:https://github.com/mobile-dev-inc/Maestro
- 社区 Slack:https://slack.maestro.dev