第一章:Espresso 简介与基础配置
说起 Android UI 测试,大家可能第一反应是“又慢又不稳定”。确实,早年我用 Robotium 或者单纯靠 Thread.sleep() 等控件时,CI 构建经常因为测试随机失败而变红,非常头疼。
后来接触了 Espresso,哎,感觉不一样了。它最核心的卖点就是自动同步。不需要你在代码里到处塞 sleep(),它会默默的等待当前进程的消息队列空闲下来,再去执行你的点击或断言操作。这种做法不仅稳定,而且测试执行速度非常快。
本章我不会给你讲太多虚的概念,我们直接上手,先把环境搭好,跑通第一个测试,看看它到底是怎么工作的。
Espresso 到底是什么?
官方定义是「用于 Android 的 UI 测试框架」。但在我看来,它本质是一个View 查找器 + 操作执行器 + 状态断言器的三合一工具。它运行在应用的进程内,所以可以直接操作目标 View,不需要 Accessibility 权限。
- ViewMatcher: 匹配器,用来在 View 树中定位目标组件。例如
withId(R.id.btn_login),withText("提交")。 - ViewAction: 动作执行器,模拟用户在控件上的操作。例如
click(),typeText("hello"),scrollTo()。 - ViewAssertion: 断言器,验证目标控件的状态。例如
matches(isDisplayed()),doesNotExist()。
你想想看,如果没有 Espresso,我们要写多少样板代码才能稳定实现「等待控件出现 -> 点击 -> 检查结果」这个流程?我以前在写自动化脚本时,经常为了等一个弹窗要加 2 秒的等待,跑一遍全量测试要花一个多小时,换成 Espresso 之后,这个时间缩短了将近 60%。
环境搭建(Gradle 配置)
配置其实没什么坑,但要注意版本兼容性。我个人习惯在 module 的 build.gradle 里统一管理依赖版本,避免混用导致冲突。
| 组件 | 依赖声明 | 说明 |
|---|---|---|
| Core | androidx.test.espresso:espresso-core:3.5.1 | 必须引入,包含 Matcher、Action、Assertion |
| Runner | androidx.test:runner:1.5.2 | 用于运行测试用例(AndroidJUnitRunner) |
| Rules | androidx.test:rules:1.5.0 | 提供 ActivityScenarioRule 等测试规则 |
| Extras | espresso-contrib | 提供 NavigationView、Drawer 等的扩展匹配器 |
build.gradle 中同时依赖了低版本的 espresso-core 和高版本的 test:runner,导致运行测试时一直报 NoClassDefFoundError。建议从 官方版本页面 查看最新的配对版本,或者在项目中锁定 androidx.test:core:1.5.0 的版本。第一个 Espresso 测试
我们直接看代码。假设有一个 Activity,上面有个 ID 为 btn_submit 的按钮,点击后会出现一个文本为“提交成功”的 TextView。测试代码大概长这样:
import androidx.test.espresso.Espresso.onView;
import androidx.test.espresso.action.ViewActions.click;
import androidx.test.espresso.assertion.ViewAssertions.matches;
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import androidx.test.espresso.matcher.ViewMatchers.withId;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class SubmitButtonTest {
@Rule
public ActivityScenarioRule<MainActivity> activityRule =
new ActivityScenarioRule<>(MainActivity.class);
@Test
public void clickSubmit_shouldShowSuccessText() {
// 1. 使用 withId 匹配器找到按钮
// 2. 执行 click 操作
// 3. 检查特定 ID 的文本是否显示
onView(withId(R.id.btn_submit))
.perform(click());
onView(withId(R.id.tv_success))
.check(matches(isDisplayed()));
}
}
这段代码看起来很简洁对吧?实际上背后 Espresso 做了不少事情。它会利用 ActivityScenarioRule 自动启动 Activity,并且在 onView 调用时等待 UI 线程空闲。如果你在真实设备上跑过,就会感受到它的流畅——点击完按钮后几乎立即执行断言,不像老框架那样需要傻等几秒。
espresso-contrib 库,它提供了 RecyclerViewActions 和 NavigationViewActions 等实用扩展。
测试运行器配置
如果你用的是 AndroidJUnitRunner(推荐),在 build.gradle 里的 defaultConfig 中添加:
android {
defaultConfig {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}
这个 Runner 负责将测试用例安装到设备上,并执行 @RunWith(AndroidJUnit4.class) 标注的类。如果不配置这一步,测试会无法启动。
同步机制:为什么它那么稳?
Espresso 的核心设计理念是「靠消息队列判断空闲」。它会注册一个 IdlingResource(空闲资源)监控器,确保在查找 View 或执行操作前,所有异步任务(如 AsyncTask、Handler post、网络回调)都已经完成。这就避免了「控件还没加载就点击」的问题。
我记得有一次在项目中测试一个列表加载,一开始用 Thread.sleep(3000) 等数据回来,偶尔还是因为网络慢导致测试失败。后来换成用 IdlingResource 来监控 OkHttp 的请求队列,测试就变得非常稳定,几乎没有再因为同步问题挂过。
当然,默认情况下 Espresso 只监控主线程的消息队列和 AsyncTask。如果你用了 RxJava、Coroutine 或者其他异步框架,就需要自己实现 IdlingResource 来告诉 Espresso「我还有任务没做完,先别急着检查」。这个机制非常重要,后续章节我们会单独深入讲自定义 IdlingResource 的实战用法。
小结
这一章我们从环境搭建到第一个测试用例都跑通了。你只需要记住三个核心 API —— onView、perform、check。它们就像三脚架一样支撑着 Espresso 的测试体系。有了这个基础,后面讲自定义匹配器时,你就能理解它是在哪个环节起作用、以及如何发挥它的威力了。