78、UI自动化测试:Espresso、UI Automator、录制回放、持续集成
说实话,UI自动化测试这个话题,很多Android开发者是又爱又恨。爱的是它能帮我们省下大量回归测试的时间,恨的是——写起来麻烦,跑起来不稳定,维护起来想骂人。
我最早接触UI自动化是在一个金融App项目里。那时候团队只有两个人写测试,每次发版前要手动点几百个页面,点得手指发麻。后来我实在受不了了,决定把Espresso和UI Automator捡起来好好搞一搞。嗯,这一搞就是三年。
今天我就把这三年的经验浓缩一下,跟你聊聊Android UI自动化测试的四个核心方向:Espresso、UI Automator、录制回放、持续集成。
核心观点:UI自动化测试不是银弹,但它能帮你把「手动点100遍」变成「一键跑100遍」。关键在于选对工具、写好用例、搭好流水线。
一、Espresso:单App内的UI测试利器
Espresso是Google官方推出的UI测试框架,专门用来测试单个App内部的UI交互。它的核心思想就一句话:在真实设备上模拟用户操作,然后验证UI状态。
我个人习惯把Espresso的用法总结成三步:
- 找控件:用
onView()配合 Matcher 找到目标 View - 做操作:用
perform()执行点击、输入等动作 - 验结果:用
check()验证UI是否符合预期
举个例子,测试一个登录按钮的点击效果:
@Test
public void testLoginButton() {
// 找到登录按钮
onView(withId(R.id.btn_login))
.perform(click()); // 点击它
// 验证是否跳转到主页
onView(withId(R.id.tv_welcome))
.check(matches(isDisplayed()));
}
看着简单吧?但实际项目中坑不少。我遇到过最典型的问题就是异步操作。比如网络请求还没回来,Espresso就已经开始检查UI了,结果测试必挂。
我的经验:遇到异步问题,别急着加 Thread.sleep()。用 IdlingResource 才是正解。它能让Espresso乖乖等到异步任务完成再继续执行。我在一个视频App项目里,就是用IdlingResource解决了90%的测试不稳定问题。
IdlingResource的用法大致是这样:
// 注册空闲资源
IdlingRegistry.getInstance()
.register(myIdlingResource);
// 执行测试
onView(withId(R.id.btn_load))
.perform(click());
// 测试结束后注销
IdlingRegistry.getInstance()
.unregister(myIdlingResource);
二、UI Automator:跨App测试的王者
Espresso只能测你自己的App,但UI Automator不一样。它能跨App操作,比如打开系统设置、操作第三方App、甚至模拟系统级事件。
说白了,UI Automator就是你的「系统级手指」。我在做支付SDK测试时,经常用它来模拟拉起支付宝或微信支付,然后验证支付结果。
核心API就两个:
- UiDevice:代表设备本身,可以按电源键、旋转屏幕、按返回键
- UiObject:代表界面上的控件,通过文本、描述、类名等方式定位
看个例子,打开系统设置里的WiFi开关:
// 获取设备实例
UiDevice device = UiDevice.getInstance(
InstrumentationRegistry.getInstrumentation());
// 按Home键回到桌面
device.pressHome();
// 打开设置App
device.executeShellCommand(
"am start -a android.settings.SETTINGS");
// 找到WiFi菜单项并点击
UiObject wifiSetting = device.findObject(
new UiSelector().text("Wi-Fi"));
wifiSetting.clickAndWaitForNewWindow();
注意:UI Automator的定位方式依赖UI文本和描述,如果App做了国际化,测试用例也得跟着适配。我曾经因为App从中文切换到英文,导致一整套UI Automator用例全部失效……嗯,从那以后我养成了用资源ID定位的习惯。
三、录制回放:快速生成测试用例
写UI测试用例最烦的是什么?是手写那些 onView(withId(...)) 的样板代码。录制回放功能就是为了解决这个问题。
Android Studio自带的 Espresso Test Recorder 可以帮你录制操作,然后自动生成测试代码。你只需要在模拟器上点几下,代码就出来了。
操作步骤很简单:
- 在Android Studio中点击
Run > Record Espresso Test - 在模拟器上执行你的操作(点击、输入、滑动等)
- 录制完成后,添加断言(比如检查某个文本是否显示)
- 保存,代码自动生成
但说实话,录制生成的代码只能当草稿用。我试过几次,生成的代码里经常有多余的等待时间、不准确的定位方式。我的建议是:
- 用录制快速搭建测试骨架
- 然后手动优化定位方式和断言逻辑
- 把公共操作(比如登录)提取成独立方法
一个小技巧:如果你不想用官方的Recorder,也可以自己写一个简单的录制工具。我在一个项目中用 AccessibilityService 监听用户操作,然后自动生成测试脚本。虽然有点重,但对于复杂交互场景特别管用。
四、持续集成:让自动化测试跑起来
UI自动化测试写好了,不跑起来就是白写。持续集成(CI)就是让测试自动运行的流水线。
我常用的CI方案有两种:
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Jenkins + 本地设备 | 小团队、内部测试 | 免费、可控 | 需要维护设备集群 |
| Firebase Test Lab | 中大型团队、云端测试 | 多设备、多版本、自动截图 | 按量付费 |
| GitLab CI + 模拟器 | 轻量级、快速反馈 | 集成方便、成本低 | 模拟器不如真机准 |
我个人比较推荐 GitLab CI + Firebase Test Lab 的组合。GitLab CI负责触发构建和上传APK,Firebase Test Lab负责在云端跑测试。这样你本地不需要维护任何设备。
一个简单的 .gitlab-ci.yml 配置片段:
ui-test:
stage: test
script:
- ./gradlew assembleDebug
- ./gradlew assembleDebugAndroidTest
- gcloud firebase test android run
--app app/build/outputs/apk/debug/app-debug.apk
--test app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk
--device model=Pixel2,version=28,locale=zh_CN
only:
- develop
避坑指南:我曾经在CI上吃过一个大亏——测试用例在本地跑全绿,上了CI就各种红。后来发现是CI上的设备语言是英文,而我的测试用例里写死了中文文本。解决方案很简单:要么在测试前设置设备语言,要么用资源ID代替文本匹配。
五、实战建议:搭建你的UI自动化测试体系
说了这么多,最后给你几个落地建议:
- 先核心后边缘:别一上来就想覆盖所有页面。先把登录、注册、支付这些核心流程测好,再慢慢扩展。
- 用例要稳定:一个不稳定的测试用例比没有更糟糕。它会让团队对自动化失去信心。我见过太多团队因为用例频繁误报,最后把CI上的UI测试给禁用了。
- 截图留证据:测试失败时自动截图,方便排查问题。Firebase Test Lab自带这个功能,Jenkins的话需要自己写插件。
- 分层设计:把公共操作(登录、导航)封装成Page Object模式,避免每个用例都重复写一遍。
最后说一句:UI自动化测试不是一蹴而就的事。它需要持续投入、持续优化。但只要你坚持下来,你会发现——发版前的焦虑少了,周末加班的次数少了,产品的质量也稳了。这笔账,怎么算都划算。
公众号:蓝海资料掘金营,微信deep3321