零基础实战:用 Maestro 从零跑通第一个跨平台 UI 自动化用例
【免费下载链接】MaestroPainless E2E Automation for Mobile and Web项目地址: https://gitcode.com/GitHub_Trending/ma/Maestro
凌晨一点,CI 里那条登录测试又红了,而本地明明刚才是绿的。你盯着日志里那句"Element not found",页面在真机上还好好显示着,用例却就是找不到。如果你也被这种偶发失败折磨过,可以试试Maestro:一个开源的跨平台 UI 自动化测试框架,你用一份 YAML 用例描述测试步骤,同一份用例可以在 Android、iOS、Web 上运行,框架内置的智能等待还会替你盯住没加载出来的元素。按这篇教程从头走到尾,你手上会有一条在真模拟器上跑通的登录流程用例,外加把它改稳、改到 Web 端的具体做法。
先看最终成品:一条 8 行的登录用例 🎯
在装任何东西之前,先看看你要做出的东西长什么样——整个测试就是一个几行长的 YAML 文件:
appId: com.example.shop tags: - login --- - launchApp: clearState: true - tapOn: "登录" - inputText: "standard_user" - inputText: "secret_sauce" - tapOn: "确认登录" - assertVisible: "我的订单"就这么几行,启动应用、输入账号密码、点进订单页、验证页面出现,全部完成。下面从环境开始,把这条用例从头走到尾。
环境一步装好并验证
前置条件只有一个:Java 17 或更高版本,Maestro 本身是 Java 写的,跑它需要 JVM 在场。macOS、Linux、Windows(WSL)都行,共三步:
java -version确认当前环境的 Java 版本不低于 17,低于就先把 JDK 升到 17 再往下走。
curl -fsSL "https://get.maestro.mobile.dev" | bash export PATH="$PATH:$HOME/.maestro/bin"第一条命令下载并执行官方安装脚本;第二条把安装目录挂进当前终端的PATH,让 shell 能找到maestro命令——新开终端时记得重复这一步,或把export写进你的 shell 配置。
maestro --version能打印出版本号,环境就算就绪了。到这一步不需要装 SDK、不需要配驱动,安装就是一个二进制。
逐行拆解:最小可运行用例长什么样
回到上面那条用例,它的结构分两半:---之前是头部,之后是步骤。
头部声明被测目标。appId写应用的包名,iOS 应用则是 bundle id——这一步告诉 Maestro 要驱动哪个应用;tags给用例打标签,后续可以按标签筛选运行,比如把所有login标签的用例挑出来单独跑。
launchApp负责把应用拉起来。这里的clearState: true值得留意:它会清掉应用上一次的运行状态再启动,保证每次测试都从一张白纸开始,避免残留的登录态、购物车这类脏数据干扰结果。
tapOn按文本定位元素。你写的是"文本为『登录』的元素",不是屏幕坐标,也不是层层嵌套的选择器路径。定位基于元素上可见的文本与语义信息,所以页面换肤、排版微调时,只要文案还在,用例就还成立——这也是同一份 YAML 能同时跑在 Android、iOS、Web 三个端上的原因。
inputText把文本填进当前激活的输入框。连续两条inputText分别对应用户名和密码,不需要额外指定"填进哪个框"。
assertVisible是断言,也是全篇最关键的一步。它等文本为"我的订单"的元素出现,出现才算通过,等待耗尽没出现就报失败。注意这个等待是框架自动做的智能等待——你不需要手写sleep,也不用猜页面要加载多久。
读下来你会发现,整条用例就是一句白话:"启动应用 → 点登录 → 输账号密码 → 提交 → 验证到了订单页。"
如果流程里需要分支,用if/then/else表达。比如某个弹窗只在特定状态下出现,就可以写成:当"同意"按钮可见时点击它,否则跳过——判断条件通常就是某个元素是否可见。
让用例跑稳:红灯的两种病因 🔧
用例跑通之后,真正折磨人的是两种失败:偶尔变红,和死活找不到元素。它们成因不同,解法也不同。
偶尔变红:把等待写明确
"这条断言本地十次红一次",多数时候是页面重渲染、网络慢半拍这类时序问题,不是逻辑写错了。两个手段可以明确等待的行为:
- retry: maxRetries: 2 commands: - assertVisible: "加载完成" - assertVisible: text: "支付成功" timeout: 15000retry把不稳的步骤包起来,整组最多重试maxRetries次;timeout把单条断言的等待上限从默认值拉长到 15 秒(单位毫秒)。
但要说清楚边界:这是等得更久,不是无限等。超了时仍然变红,问题就不在等待时长,而在断言本身或前置步骤——继续加码等待只是把失败推迟,不会把失败修好。仓库里e2e/workspaces/simple_web_view/webview.yaml就是这个思路的真实例子:对一个偶发被吞掉的点击套retry,再接extendedWaitUntil把等待上限拉到 90 秒并配上说明标签。
找不到元素:按成本从低到高的排查顺序
"Element not found" 大多数时候不是元素真的消失,而是定位条件给得太死。按排查成本从低到高走三步:
换成模糊匹配。文本里带单号、时间戳这类动态内容时,别整串精确匹配,用
contains只钉住其中一段:- tapOn: text: contains: "订单"用父子层级限定范围。页面上有多个同文本元素(两个"提交")时,加
parent把搜索范围限到指定父容器里,目标立刻唯一。确认前置状态成立。页面可能根本没翻到正确位置,或应用状态被前一条用例污染了。给
launchApp加上clearState: true,在干净环境里重跑一次;问题消失,说明要显式固定初始状态。
同一用例上 Web:换一个头部就够 🌐
移动端和 Web 端的差别只在头部那一行:移动端用appId声明包名,Web 端改用url字段直接指向页面地址,后面的launchApp、tapOn、inputText与移动端完全同构——逻辑只写一遍,元素在各端找得到就行。
仓库里e2e/workspaces/web/simple.yaml就是一份现成的 Web 用例:头部是url: https://www.saucedemo.com/,之后输入用户名密码、点 Login,断言商品列表出现,和前面那条登录用例的结构一一对应。
另外提一句:如果用例要反复造随机测试数据,比如注册接口需要一个不重样的邮箱,用inputRandomEmail、inputRandomNumber这类命令在运行时现场生成,不必把测试数据写死在用例里。
交接前对照这份清单收尾
一条用例能跑,和"可以交给别人或 CI 的用例集"之间还差几件事。逐条打勾:
- ☑单平台连续 10 次全绿:在 Android 或 iOS 模拟器上连跑 10 遍,零失败。
- ☑负向分支已覆盖:用一个会被拒绝的账号跑通,断言的是错误提示出现,而不是用例卡死。
- ☑Web 版本改写完成:同一套步骤用
url头部跑通浏览器端。 - ☑偶发失败的断言已加
retry或timeout:且观察 20 次以上不再单独变红。 - ☑随机数据不落盘:账号、邮箱等测试数据用运行时生成,不写死在用例里。
五条全勾上之后,真正值得盯的目标就一个:同一条 Maestro 用例,在 Android、iOS、Web 三个端上轮流跑,稳定变绿——到那时,这份 YAML 才配叫跨平台。
【免费下载链接】MaestroPainless E2E Automation for Mobile and Web项目地址: https://gitcode.com/GitHub_Trending/ma/Maestro
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考