Espresso 是一个由 Google 团队开发、属于 AndroidX Test 组件的 Android 应用程序自动化 UI 测试框架。与检查封闭组件的工具测试不同,Espresso 与真实 UI 交互:按下按钮、输入文本、检查元素显示。据 Google Android Developers 报道,Espresso 提供与 UI 线程的自动同步,消除了手动 Thread.sleep() 的必要性。
核心要点
Espresso 是一个用于编写自动化 Android UI 测试的库,属于 Google AndroidX Test 的一部分。它提供了用于在屏幕上查找 View 元素、对它们执行操作(点击、输入、滑动)以及检查它们的状态(显示、包含文本、活跃)的 API。
Espresso 的关键特点是与应用程序主线程的自动同步。框架在执行下一步检查之前等待所有异步任务(协程、AsyncTask、Handler)的完成。这消除了与竞争条件相关的不稳定测试,并使 UI 测试稳定可靠 — 没有任何测试包含 Thread.sleep() 或等待循环。
Espresso 遵循 Three-Legged Dog(三脚狗)原则 — 测试由三个步骤组成:查找元素(ViewMatcher)、执行操作(ViewAction)、检查结果(ViewAssertion)。所有三个步骤都写在 onView().perform().check() 的链式调用中。这种概念使测试可预测并易于阅读 — 每个测试明确描述它在搜索什么、做什么以及检查什么。
架构 Espresso 基于三个组件:Espresso(入口点 — 静态方法 onView 和 onData)、ViewMatchers(搜索元素)、ViewActions(操作)和 ViewAssertions(检查)。在内部,框架使用 Idling Resource 与 UI 线程同步。
最简单的测试根据 ID 查找按钮,执行点击并检查“完成”文本是否出现。从测试的角度看,所有操作都是同步执行的 — Espresso 保证 UI 线程在测试继续执行之前已完成事件处理。这是通过内置的等待机制实现的:onView 会阻塞测试执行,直到 UI 变得稳定。
@Test
fun buttonClick_showsSuccessText() {
// 按 ID 查找按钮并点击
onView(withId(R.id.button_submit))
.perform(click())
// 检查“完成”文本是否显示
onView(withText("完成"))
.check(matches(isDisplayed()))
}
为了启动 Espresso 测试,使用 ActivityScenario (AndroidX Test) 来创建所需状态的 Activity — 运行中、暂停、销毁。ActivityScenario 允许在纯 UI 之外测试 Activity 的生命周期。例如,可以检查数据是否在屏幕旋转时(Activity 重新创建)保存并在销毁后恢复。
ViewMatchers 是来自 Espresso.onView 类的一组方法,可以根据不同标准在屏幕上查找 View:资源标识符 (R.id)、文本、提示信息、父元素和层级结构。如果单个 matcher 没有唯一结果,可以通过 allOf() 组合 matcher。
| Matcher | 用途 |
|---|---|
| withId(R.id.name) | 根据资源 ID 查找 |
| withText(“文本”) | 根据显示的文本查找 |
| withHint(“提示”) | 根据 EditText 的 hint 属性查找 |
| isDisplayed() | 检查元素是否在屏幕上可见 |
| hasSibling(matcher) | 根据邻近元素查找 |
| allOf(m1, m2) | 组合多个 matcher |
如果屏幕上有多个相同元素(例如,两个具有不同文本的 TextView),可以通过 allOf 组合 matcher:onView(allOf(withId(R.id.title), withText(“你好”)))。这保证选择唯一的元素。反向运算符 not() 从搜索中排除元素,而 hasSibling() 在已知元素旁边查找元素。
ViewActions 是 Espresso 对查找到的 View 执行的操作:click()、typeText()、clearText()、scrollTo()、swipeLeft() 等。操作传递给 perform() 方法,它可以接受多个连续的操作。
perform() 方法接受 vararg ViewAction,允许对一个元素执行一系列操作:清除字段、输入新文本、关闭键盘并按下按钮。所有操作按列表顺序执行,Espresso 保证前一个操作完成后下一个才开始。
// 在 EditText 中输入文本并点击按钮
onView(withId(R.id.edit_email))
.perform(
clearText(),
typeText("user@example.com"),
closeSoftKeyboard()
)
onView(withId(R.id.button_login))
.perform(click())
对于 AdapterView 内的元素(ListView、RecyclerView),使用 onData() 方法代替 onView。它与适配器数据工作,而非 View — 根据模型内容查找元素并返回相应的 View 以供后续操作。onData 使用 hamcrest matcher 根据数据模型的字段查找元素。
ViewAssertions 检查 View 是否处于特定状态。基础方法 matches(matcher) 检查元素是否符合给定的 matcher。此外,Espresso 提供 doesNotExist()(元素不存在)和 selectedDescendantsMatch()(检查嵌套元素)。
UI 测试中最常见的检查:元素显示 (isDisplayed)、元素包含特定文本 (withText)、元素活跃 (isEnabled)、元素未选中 (isNotChecked)。每个检查在失败时都会抛出详细的异常 — 包含屏幕上 View 层级结构的说明。这简化了调试:在错误消息中可以看到检查时刻屏幕上实际存在哪些元素。
如果标准检查不足,可以通过 ViewAssertion 接口创建自己的检查。自定义 assertion 接收 View 并可以用程序方式检查其状态 — 例如,文本颜色、边距或未通过标准 matcher 暴露的自定义组件状态。
// 检查:TextView 显示并包含文本
onView(withId(R.id.text_welcome))
.check(matches(isDisplayed()))
.check(matches(withText("欢迎")))
// 检查:元素未显示
onView(withId(R.id.progress_bar))
.check(doesNotExist())
Idling Resource 是 Espresso 用于同步测试与异步操作的机制。默认情况下,Espresso 等待 Handler、AsyncTask 和协程(通过 coroutinesIdlingResource)完成。如果应用程序通过自己的线程或 Callback 服务执行后台工作,则需要注册自定义 Idling Resource。
从 AndroidX Test 1.4.0 开始,Espresso 通过 CoroutinesIdlingResource 支持协程。测试会自动等待所有启动的协程完成后再执行 UI 检查。对于更复杂的场景,使用 CountingIdlingResource — 一个计数器,在任务开始时增加并在完成时减少。
// 为 OkHttp 注册 IdlingResource
class OkHttpIdlingResource(
private val client: OkHttpClient
) : IdlingResource {
private var isIdle = true
private var watcher: IdlingResource.ResourceCallback? = null
override fun getName() = "OkHttp"
override fun isIdleNow() = isIdle
override fun registerIdleTransitionCallback(
callback: IdlingResource.ResourceCallback
) {
watcher = callback
}
}
连接 在 Android 项目中连接 Espresso 是通过在模块级别的 build.gradle 中添加依赖来完成的。Espresso 是 AndroidX Test 的一部分,因此只需要指定 Espresso 核心、扩展和 JUnit 集成的依赖。测试放置在 src/androidTest 目录中,并通过 AndroidJUnitRunner 在物理设备或模拟器上运行。
最小依赖集包括 espresso-core(核心)、espresso-contrib(针对 RecyclerView、Drawer、Picker 的额外 matcher)和 runner(AndroidX 测试运行器)。所有测试通过 Android Test Orchestrator 在模拟器或物理设备上运行。
// build.gradle.kts (androidTest dependencies)
android {
defaultConfig {
testInstrumentationRunner =
"androidx.test.runner.AndroidJUnitRunner"
}
}
dependencies {
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
androidTestImplementation("androidx.test.espresso:espresso-contrib:3.6.1")
androidTestImplementation("androidx.test:runner:1.6.1")
androidTestImplementation("androidx.test:rules:1.6.1")
}
Espresso 测试可以通过 Google Android Test Orchestrator 运行,它将每个测试隔离在单独的进程中并在运行之间清除状态。这消除了与前一次测试的剩余数据相关的不稳定测试,并提高了 CI 服务器上的稳定性。对于并行运行,使用 sharding — 将测试分配到多个模拟器上。
常见问题
Espresso 在应用程序进程内工作,使用与 UI 线程的自动同步。UI Automator 在系统层面工作,可以与其他应用程序交互,但需要手动管理等待。
这是来自 Google 演示的比喻:Espresso 测试依赖三个支柱 — ViewMatcher(搜索)、ViewAction(操作)和 ViewAssertion(检查)。如果移除其中任何一个,测试就会失去稳定性,就像一只三脚狗。
对于 RecyclerView,使用 espresso-contrib 库和 onView(withId(R.id.recycler)).perform(actionOnItemAtPosition(0, click())) 方法。替代方案是使用 onData() 处理 AdapterView,或自定义 ViewAction 来根据文本在 RecyclerView 内查找元素。此外,可以使用 espresso-contrib 中的 RecyclerViewActions 滚动到元素并对其执行操作。
不稳定测试 (flaky test) 是指由于竞争条件或异步性而在代码未变的情况下偶尔失败的测试。Espresso 通过 Idling Resource 解决这个问题 — 在执行检查之前等待所有后台任务完成。
Espresso 本身不是为截图测试设计的,但可以与像 Shot 或 Paparazzi 这样的库组合使用。Espresso 将 UI 准备到所需状态,比较库拍摄截图并与参考图进行比较。这种方法称为视觉回归测试,有助于发现界面中的意外变化。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。