以前我们的发版流程是这样的:测试同学拿着两台手机,把登录、下单、支付等几十个流程手动点一遍。点完半天过去了。
这周我决定把核心流程自动化。查了半天,鸿蒙主要用的是 @ohos/hypium 结合 @ohos/uitest。写起来像写脚本一样,但跑起来的时候,各种玄学问题层出不穷。
这篇复盘,记录我用 UITest 踩过的坑。
给组件加 ID 是个好习惯
UI 测试的第一步是找组件。如果你用文本去找,比如 ON.text('登录'),极其脆弱。文案一改,你的脚本全废。
正确的做法是,在所有的业务组件上,老老实实加上 .id():
typescript
// 在开发页面时
Button('登录')
.id('btn_login')
在测试脚本里,直接用 ID 定位:
typescript
import { Driver, ON } from '@ohos.uitest';
const driver = Driver.create();
const btn = await driver.findComponent(ON.id('btn_login'));
await btn.click();
导致白屏的罪魁祸首
我写的第一版脚本,是这样的:
typescript
// 错误示范
await driver.findComponent(ON.id('btn_login')).click();
// 立刻去断言首页元素
const title = await driver.findComponent(ON.text('首页'));
expect(title !== null).assertTrue();
跑一次,失败一次。因为网络请求是异步的,点击登录后,页面还在转圈,系统根本找不到"首页"这两个字,脚本直接超时崩溃。
重构方案: 必须使用 waitForComponent。它会在指定时间内轮询查找,直到元素出现。
typescript
await driver.findComponent(ON.id('btn_login')).click();
// 最多等待 5000 毫秒
const title = await driver.waitForComponent(ON.text('首页'), 5000);
expect(title !== null).assertTrue();
系统弹窗与权限
跑自动化时,最怕的不是业务逻辑不对,而是系统突然弹个框。
比如你正在测"扫一扫",系统弹个"是否允许使用相机"。你的脚本一懵,直接卡死在那里。
处理办法: 在测试框架的 beforeAll 钩子里,或者每个测试用例开始前,先做一次防御性检查。
typescript
async function handleSystemDialog(driver: Driver) {
// 找一下有没有“允许”按钮,如果有就点掉
const allowBtn = await driver.findComponent(ON.text('允许'));
if (allowBtn) {
await allowBtn.click();
}
}
另外,测试用例之间一定要有独立性。测试完登录,要把数据清理掉,退回初始状态,别让上一个用例的状态影响下一个。
-
UI 测试不要贪多,挑最核心的链路写(如登录、支付),边缘业务还是靠人工。
-
所有操作必须包裹
waitForComponent,否则跑一次挂一次。 -
将测试脚本接入 CI/CD(比如 GitLab CI),每次提 PR 自动跑一遍,那酸爽,谁用谁知道。