进阶篇
掌握变量、条件、循环、分支,写出灵活的自动化流程。
变量与环境变量 env / defineVariables
在配置区定义(env)
appId: com.example.app
env:
USERNAME: admin
PASSWORD: "123456"
API_URL: "https://api.example.com"
---
- inputText: "${USERNAME}" # 使用变量在命令区定义(defineVariables)
- defineVariables:
counter: "0"
greeting: "你好"
- inputText: "${greeting}" # 会输入:你好区别
env | defineVariables | |
|---|---|---|
| 位置 | 配置区(--- 上方) | 命令区(--- 下方) |
| 时机 | 流程开始前就生效 | 执行到这一行时生效 |
| 用途 | 全局配置(账号密码等) | 流程中动态定义变量 |
模板表达式 $
在任何命令参数中使用 ${...} 来引用变量或执行 JavaScript 表达式。
引用变量
- inputText: "${USERNAME}"数学运算
- sleep: ${Math.random() * 5000} # 随机 0~5 秒字符串拼接
- evalScript: "${fullName = firstName + ' ' + lastName}"
- inputText: "${fullName}"条件表达式
- assertTrue: "${counter > 0}"求值时机
${...} 在命令执行时才求值,但有一个重要规则:
repeat的commands和branch内部的表达式不会被提前计算- 它们在每次执行到的时候才计算,所以
${Math.random()}每次循环都能得到不同的随机值
- repeat:
times: "3"
commands:
# 每次循环都会重新求值 Math.random(),得到不同随机数
- sleep: ${Math.random() * 5000}转义 ${}
如果你需要输入字面意义的 ${...},在前面加反斜杠:
- inputText: "价格是 \\${amount} 元" # 实际输入:价格是 ${amount} 元元素选择器详解
前面用的 tapOn: "登录" 只是最简单的文字匹配。选择器支持非常多的匹配方式。
文字匹配 text
- tapOn:
text: "登录" # 文字匹配(不区分大小写)正则匹配
- tapOn:
text: "登录.*" # 匹配以"登录"开头的任何文字
- tapOn:
text: "用户名|Username" # 匹配"用户名"或"Username"ID 匹配
- tapOn:
id: "btn_login" # 通过资源 ID 匹配索引 index
当屏幕上有多个同名元素时,用 index 指定第几个:
- tapOn:
text: "删除"
index: 0 # 第一个"删除"按钮(从 0 开始)
- tapOn:
text: "删除"
index: -1 # 最后一个"删除"按钮状态匹配
- tapOn:
id: "checkbox"
enabled: true # 只匹配可用的元素
checked: false # 只匹配未勾选的元素支持的状态:enabled、selected、checked、focused。
空间关系(相对位置)
当一个元素自身没有独特标识时,可以通过它和另一个元素的空间关系来定位。
# 点击"用户名"下方的输入框
- tapOn:
below: { text: "用户名" }
# 点击"密码"标签右边的输入框
- tapOn:
rightOf: { text: "密码" }四个方向:below、above、leftOf、rightOf。
层级关系
# 点击 container 容器内部的按钮
- tapOn:
text: "确定"
childOf: { id: "dialog_container" }
# 点击包含"VIP"子元素的卡片
- tapOn:
containsChild: { text: "VIP" }
# 点击同时包含多个子元素的容器
- tapOn:
containsDescendants:
- text: "标题"
- text: "副标题"尺寸匹配
- tapOn:
width: 200
height: 100
tolerance: 5 # 允许 ±5 像素误差元素特性 traits
按元素的可交互特性来匹配,多个特性用空格分隔:
- tapOn:
traits: "clickable enabled" # 匹配可点击且可用的元素optional 标记
- tapOn:
text: "跳过广告"
optional: true # 找不到就跳过,不报错label 标记(日志可读性)
- tapOn:
id: "btn_submit"
label: "点击提交按钮" # 日志中会显示这个描述完整示例
- tapOn:
text: "确定"
below: { text: "您确定要删除吗?" }
childOf: { id: "confirm_dialog" }
enabled: true
optional: true
label: "点击确认删除按钮"长按 longPressOn
长按某个元素。
- longPressOn: "设置"
- longPressOn:
text: "文件.txt"
waitToSettleTimeoutMs: 3000双击 doubleTapOn
双击某个元素。
- doubleTapOn: "图片"
- doubleTapOn:
text: "收藏"
delay: 250 # 两次点击之间的间隔(默认 100 毫秒)
waitToSettleTimeoutMs: 3000 # 双击后等待界面稳定,最多等 3 秒滚动查找 scrollUntilVisible
不停滚动页面,直到目标元素出现为止。
- scrollUntilVisible:
element:
text: "关于本机" # 要找的元素
direction: DOWN # 滚动方向
timeout: "30000" # 最多滚动 30 秒所有参数
- scrollUntilVisible:
element: # 必填:要找的元素选择器
text: "目标元素"
direction: DOWN # 方向:UP / DOWN / LEFT / RIGHT,默认 DOWN
timeout: "20000" # 超时:毫秒,默认 20000
speed: "40" # 速度:0(最慢)~ 100(最快),默认 40
visibilityPercentage: 100 # 元素可见比例 0~100,默认 100
centerElement: false # 是否尝试将元素滚动到屏幕中央,默认 false
from: # 选填:在指定容器内滚动(见高级篇-容器内滚动)
id: "list_container"
waitToSettleTimeoutMs: 3000 # 选填:每次滚动后等待界面稳定的超时实际例子:在设置中找到"关于本机"
- launchApp
- scrollUntilVisible:
element:
text: "关于本机"
direction: DOWN
timeout: "30000"
- tapOn: "关于本机"条件执行 when
给任何命令添加 when 条件——只有条件成立时才执行。
元素可见时执行
- tapOn:
text: "跳过"
when:
visible: { text: "广告" } # 只有屏幕上有"广告"时,才点击"跳过"元素不可见时执行
- tapOn:
text: "登录"
when:
notVisible: { text: "欢迎" } # 没有"欢迎"文字时,才点击"登录"脚本条件
- tapOn:
text: "下一页"
when:
true: "${counter < 10}" # counter 小于 10 时才执行when 支持的所有字段
| 字段 | 类型 | 说明 |
|---|---|---|
visible | 选择器 | 元素可见时为 true |
notVisible | 选择器 | 元素不可见时为 true |
true | 字符串 | JS 表达式结果为真时为 true |
label | 字符串 | 日志标签 |
多个字段同时写时,全部满足才执行(AND 逻辑)。
概率执行 chance
让命令以一定概率执行——适用于需要随机化操作的测试场景。
- tapOn:
text: "跳过"
optional: true
chance: 0.3 # 30% 概率点击跳过- tapOn:
text: "查看详情"
optional: true
chance: 0.1 # 10% 概率查看详情chance 取值范围 0.0 ~ 1.0:
0.0= 永远不执行0.5= 50% 概率1.0= 总是执行
提示:
chance通常搭配optional: true使用。因为如果概率命中但元素不存在,不加 optional 会报错。
循环 repeat
固定次数循环
- repeat:
times: "5"
commands:
- scroll
- sleep: 1000执行 5 次:滚动 → 等待 1 秒。
按时间循环
- repeat:
duration: "60000" # 循环 60 秒(1 分钟)
commands:
- swipe: { direction: UP }
- sleep: [3000, 8000]持续执行 1 分钟。
随机时长循环
- repeat:
duration: [300000, 600000] # 随机 5~10 分钟(启动时随机取一个值)
commands:
- scroll
- sleep: [3000, 8000]注意:
duration的数组形式在流程启动时随机选定一个值,整个循环过程使用同一个时长。
次数 + 时间(取先到者)
- repeat:
times: "50" # 最多 50 次
duration: "600000" # 或最多 10 分钟
commands:
- scroll条件循环(while)
- repeat:
while:
visible: { text: "加载中..." }
commands:
- sleep: 1000 # 只要"加载中..."还在,就一直等- defineVariables:
counter: "0"
- repeat:
while:
true: "${counter < 10}" # counter < 10 时持续循环
commands:
- evalScript: "${counter = counter + 1}"
- tapOn: "下一个"限制:单个 repeat 最多执行 1000 次循环,防止无限循环。默认最大时长 30 分钟。
条件分支 branch
类似编程语言中的 if / else if / else——从上到下检查条件,执行第一个满足条件的分支。
- branch:
# if:屏幕上有"从图片库中选择"
- when: { visible: "从图片库中选择" }
commands:
- tapOn: "从图片库中选择"
# else if:屏幕上有"更换照片"
- when: { visible: "更换照片" }
commands:
- tapOn: "更换照片"
- tapOn: "从图片库中选择"
# else:以上都不满足
- commands:
- back搭配脚本条件
- branch:
- when: { true: "${counter > 10}" }
commands:
- tapOn: "完成"
- when: { true: "${counter > 5}" }
commands:
- tapOn: "继续"
- commands:
- tapOn: "开始"子流程 runFlow
把一组命令包装成子流程,可以搭配条件、概率使用。
内联命令
- runFlow:
commands:
- tapOn: "设置"
- tapOn: "账户"
- back引用外部文件
- runFlow: "subflow/common-auth.yaml"
# 或对象形式
- runFlow:
file: "subflow/common-auth.yaml"注意:
file和commands不能同时使用,二选一。
条件执行的子流程
- runFlow:
when:
visible: { text: "登录" }
commands:
- tapOn: "登录"
- inputText: "${PASSWORD}"
- tapOn: "确定"概率执行的子流程
- runFlow:
chance: 0.1 # 10% 概率执行反馈流程
commands:
- tapOn: "反馈"
- inputText: "测试反馈"
- pressKey: Enter
- back子流程带变量
- runFlow:
env:
TOKEN: "${authToken}"
commands:
- httpRequest:
url: "https://api.example.com"
headers:
Authorization: "Bearer ${TOKEN}"异常处理器 exceptionHandlers
自动处理意外弹窗(权限请求、广告、系统提示等)。在配置区定义,全局生效。
工作原理
- 某条命令执行失败(比如找不到元素)
- 引擎检查当前屏幕是否有匹配异常处理器的弹窗
- 如果有,自动点击它来关闭弹窗
- 然后重新尝试原来失败的命令
简写形式
appId: com.example.app
exceptionHandlers:
- "允许" # 自动点击"允许"
- "我知道了" # 自动点击"我知道了"
- "以后再说" # 自动点击"以后再说"
---
- launchApp对象形式(高级)
exceptionHandlers:
- text: "跳过"
maxTriggerCount: 3 # 最多触发 3 次,之后不再处理
- id: "close_button"
below: { text: "广告" } # 支持空间关系选择器自定义等待 extendedWaitUntil
等待某个元素出现或消失,可以自由设定超时时间(assertVisible 的超时是固定的 17 秒,这里可以自定义)。
等待元素出现
- extendedWaitUntil:
visible:
text: "加载完成"
timeout: "30000" # 最多等 30 秒等待元素消失
- extendedWaitUntil:
notVisible:
id: "loading_spinner"
timeout: "15000" # 等"加载中"消失,最多 15 秒进阶实战:信息流滑动 2 分钟
appId: com.example.demo
name: 信息流滑动 2 分钟
---
# 启动 Demo App 并确认进入推荐页
- launchApp
- assertVisible: "推荐"
# 持续滑动 2 分钟
- repeat:
duration: "120000"
commands:
# 向上滑动,切换到下一条内容
- swipe:
direction: UP
duration: [200, 500]
waitToSettleTimeoutMs: 0
# 每条内容停留 3~8 秒
- sleep: [3000, 8000]
# 结束
- stopApp掌握了流程控制?接下来解锁全部能力 → 高级篇
