SuperAPI AI 工具集

writing-espresso-tests

测试与质量

查看项目

更新于 10-16 13:32

技能介绍

用途

本技能用于为使用 Android Views 构建的 UI(XML 布局、AppCompat、Material 组件)编写 Espresso 3.7.0 仪器化 UI 测试。Espresso 是 Android Views 的标准 UI 测试框架,不适用于 Jetpack Compose(Compose 请使用 compose/ 技能集,混合宿主参见 compose/interop/testing-with-espresso-interop/SKILL.md)。

适用时机:测试使用 Views 构建的 Activity、Fragment 或 Dialog;测试报错 NoMatchingViewException、AmbiguousViewMatcherException 或 PerformException;点击 AlertDialog 按钮无反应;需要打桩 Intent.startActivityForResult(相机、联系人、分享);需要为 Espresso 无法感知的耗时任务注册 IdlingResource;调试 RecyclerView 交互;或使用 BottomNavigationViewActions 时发现 Espresso 3.7.0 contrib 中没有该类。

不适用时机:UI 为 Jetpack Compose;测试跨越应用边界(设置、拨号、系统 UI,会抛出 InjectEventSecurityException,应改用 UiAutomator);runner 栈尚未搭建;Activity 尚未启动(应与 ActivityScenario 配合)。

主要能力

通过 onView(matcher) 查找视图,匹配器为 Hamcrest Matcher<View>。优先使用身份匹配器 withId(R.id.button)、withTagKey(int)、withResourceName(String),文本/描述作为回退。常用状态匹配器包括 isDisplayed、isCompletelyDisplayed、isEnabled、isChecked、isSelected、withEffectiveVisibility、withAlpha 等;层级匹配器包括 withParent、hasDescendant、withChild、isRoot、isAssignableFrom(Class) 等。

通过 .perform(action) 执行动作,来自 ViewActions:click、longClick、doubleClick、typeText、replaceText、clearText、pressKey、pressImeActionButton、closeSoftKeyboard、swipeUp/Down/Left/Right、scrollTo 等。

通过 .check(...) 断言,来自 ViewAssertions:matches(matcher) 在视图匹配时通过,doesNotExist() 在匹配器解析到零个视图时通过(注意:视图存在但不可见时它不通过,应使用 matches(not(isDisplayed())))。

每个 onView().perform() 与 .check() 内部都会:在主线程提交动作,调用 uiController.loopMainThreadUntilIdle() 直到主 MessageQueue 为空、已注册的 IdlingResource 报告空闲、非主 Looper 空闲、AsyncTask 线程池空闲;解析视图(0 个抛出 NoMatchingViewException,2 个及以上抛出 AmbiguousViewMatcherException);检查约束(失败抛出 PerformException);执行动作或断言;再次循环主线程至空闲。本地通过但 CI 失败的测试通常是缺少针对非 Looper 异步路径(自定义 ExecutorService、OkHttp dispatcher)的 IdlingResource。

对话框与弹窗需用 .inRoot(...) 覆盖根。RootMatchers.DEFAULT 排除对话框、弹窗与 Toast,因此对 AlertDialog 按钮使用 inRoot(isDialog()),对弹出菜单和 Toast 使用 inRoot(isPlatformPopup())。

RecyclerView 使用 contrib 的 RecyclerViewActions,如 scrollToPosition、actionOnItemAtPosition、scrollTo、actionOnItem。这些动作要求 isAssignableFrom(RecyclerView.class) 且 isDisplayed()。AdapterView(ListView、Spinner)使用 onData(...) 而非 onView,onData 会先滚动适配器使条目可见。

Intent 打桩与验证使用 IntentsRule(3.5+)或在 @Before/@After 手动 Intents.init()/Intents.release(),配合 intending/respondWith 与 intended 进行验证。IntentsTestRule 已废弃。

对于非 Looper 异步工作,通过实现 IdlingResource 并注册到 IdlingRegistry 来暴露空闲状态,配合 IdlingPolicies 设置超时。

使用方式

依赖配置:

dependencies {
    androidTestImplementation("androidx.test.espresso:espresso-core:3.7.0")
    androidTestImplementation("androidx.test.espresso:espresso-contrib:3.7.0")     // RecyclerViewActions, PickerActions
    androidTestImplementation("androidx.test.espresso:espresso-intents:3.7.0")     // Intents.intended/intending
    androidTestImplementation("androidx.test.espresso:espresso-accessibility:3.7.0")
    androidTestImplementation("androidx.test.espresso:espresso-web:3.7.0")         // WebView
    implementation("androidx.test.espresso:espresso-idling-resource:3.7.0")        // for production code that exposes IdlingResources
}

android.testOptions.animationsDisabled = true   // hermetic

注意 :espresso-idling-resource:3.7.0 应放在 implementation(生产代码)而非 androidTestImplementation,因为由生产代码增减计数。

适用场景

适用于对 Android Views UI 进行端到端断言、模拟点击/输入/手势、验证 RecyclerView 与 AdapterView 条目交互、打桩与校验跨组件 Intent、为异步任务注册 IdlingResource 以保证测试稳定性的场景。

限制

不覆盖 Jetpack Compose;不适用于跨应用边界的测试;BottomNavigationViewActions 在 espresso-contrib 3.7.0 中不存在,需改用 isDescendantOfA(isAssignableFrom(BottomNavigationView::class.java)) 直接点击底层菜单项视图;PickerActions.setDate 的月份参数使用 1-12 约定(内部会减 1 后调用 DatePicker.updateDate);不应依赖 Thread.sleep 等待动画或异步,应使用 IdlingResource 与 animationsDisabled;IntentsTestRule 已废弃。