如何用Espresso测试App跳转GitHub链接至Chrome并完成断言验证
搞定你的Espresso测试断言与Chrome跳转验证
我来帮你解决这两个Espresso测试的核心问题——列表展示断言和Chrome跳转验证,直接上干货!
一、验证搜索后仓库列表正常展示
首先得处理异步加载的问题,因为搜索GitHub仓库是网络请求,Espresso默认不会等待异步操作完成,所以咱们得先确保数据加载完毕再做断言。
1. 处理异步加载(关键!)
如果你的应用用了Retrofit、Coroutines这类异步框架,最好用IdlingResource来让Espresso等待请求完成。比如自定义一个网络请求的IdlingResource,注册到Espresso的IdlingRegistry里,这样测试会自动等数据加载完再继续。
要是不想自定义IdlingResource,也可以用Espresso的waitForView临时调试,不过IdlingResource是更优雅的方案。
2. 断言列表展示的两种方式
方式1:断言列表有至少一个item
可以自定义一个RecyclerView的item数量断言器,或者用Hamcrest匹配器:
// 自定义断言器示例 class RecyclerViewItemCountAssertion(private val matcher: Matcher<Int>) : ViewAssertion { override fun check(view: View, noViewFoundException: NoMatchingViewException?) { if (noViewFoundException != null) throw noViewFoundException val recyclerView = view as RecyclerView val adapter = recyclerView.adapter ?: throw AssertionError("RecyclerView adapter is null") assertThat(adapter.itemCount, matcher) } companion object { fun greaterThan(count: Int): RecyclerViewItemCountAssertion { return RecyclerViewItemCountAssertion(GreaterThan(count)) } } } // 测试代码里用这个断言 onView(withId(R.id.repo_recycler_view)) .check(RecyclerViewItemCountAssertion.greaterThan(0))
方式2:断言某个预期的仓库内容存在
如果知道搜索后一定会出现某个仓库(比如搜索"android"会有google/android),可以直接匹配item的文本:
// 输入搜索词并触发搜索 onView(withId(R.id.search_input)) .perform(typeText("android"), closeSoftKeyboard()) onView(withId(R.id.search_button)) .perform(click()) // 断言列表里有目标仓库 onView(withId(R.id.repo_recycler_view)) .perform(RecyclerViewActions.scrollTo<RepoViewHolder>(hasDescendant(withText("google/android")))) .check(matches(hasDescendant(withText("google/android"))))
二、验证点击仓库后Chrome打开对应网页
这个需要用到Espresso-Intents库来捕获和验证应用发出的Intent,因为打开Chrome是通过系统Intent跳转的。
1. 添加依赖
先在build.gradle里加Espresso-Intents的依赖:
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.5.1'
2. 测试代码示例
@Test fun clickFirstRepo_OpensChromeWithCorrectUrl() { // 1. 先完成搜索,确保列表加载完成 onView(withId(R.id.search_input)) .perform(typeText("android"), closeSoftKeyboard()) onView(withId(R.id.search_button)) .perform(click()) // 等待列表加载(这里用之前的IdlingResource或者等待逻辑) // 2. 初始化Intents,准备捕获Intent Intents.init() try { // 3. 点击列表首个item onView(withId(R.id.repo_recycler_view)) .perform(RecyclerViewActions.actionOnItemAtPosition<RepoViewHolder>(0, click())) // 4. 验证Intent是否符合预期:打开Chrome,URL正确 // 这里可以先获取首个item的实际URL,不用硬编码 val firstRepoUrl = getFirstRepoUrl() // 你需要自己实现这个方法,比如从ViewModel/Adapter取数据 intended(allOf( hasAction(Intent.ACTION_VIEW), hasData(Uri.parse(firstRepoUrl)), hasPackage("com.android.chrome") // 指定Chrome的包名 )) } finally { // 5. 释放Intents资源 Intents.release() } } // 示例:从Adapter获取首个仓库的URL private fun getFirstRepoUrl(): String { val scenario = ActivityScenario.launch(MainActivity::class.java) var repoUrl = "" scenario.onActivity { activity -> val recyclerView = activity.findViewById<RecyclerView>(R.id.repo_recycler_view) val adapter = recyclerView.adapter as RepoAdapter repoUrl = adapter.repos[0].url } return repoUrl }
3. 关键注意点
- 确保测试设备上安装了Chrome,并且应用有权限打开外部应用(一般默认允许,但如果有运行时权限,测试里要提前授予)。
- 如果你的应用支持选择浏览器,而你需求是强制跳Chrome,直接指定
hasPackage("com.android.chrome")就能精准验证。
最后小提示
- 测试前最好清空应用缓存,避免之前的搜索结果干扰。
- 用
ActivityScenario来启动Activity,比旧的ActivityTestRule更灵活。
内容的提问来源于stack exchange,提问作者philomath
相关产品推荐
相关产品推荐

