Espresso 测试框架(Android 4.0.1,API 级别 14 或更高版本)提供了 Android UI 测试的 API,用来模拟用户与应用的 视图交互。本文从实践的角度,讲下 Espresso 的使用,希望也能为大家避坑。关于更过 Espresso 的介绍,可点击 官网传送门 。
本 demo androidTest 自动化效果:
Espresso
在集成 Espresso 的时候,可能会遇到一些坑,主要是些依赖的版本冲突问题。具体版本没有要求,如果编译不通过,可参考下我的 demo:
在 gradle
文件中我引用了这几个 androidTest
库:
androidTestImplementation "androidx.test.ext:junit:1.1.4-alpha06"
androidTestImplementation "androidx.test:runner:1.5.0-alpha03"
androidTestImplementation "androidx.test:core:1.4.1-alpha06"
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0-alpha05'
如果使用 kotlin,还需要引入:
androidTestImplementation "androidx.test.ext:junit-ktx:1.1.4-alpha06"
androidTestImplementation "androidx.test:core-ktx:1.4.1-alpha06"
androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10"
注意 espressoVersion
的当前最新版 3.5.0-alpha06
测着有问题,ViewMatchers
时会报错,可以使用 3.5.0-alpha05
。
java.lang.IncompatibleClassChangeError: androidtest class 'org.hamcrest.stringdescription' does not implement interface 'java.lang.iterable' in call to 'java.util.iterator java.lang.iterable.iterator()'
API 组件
Espresso 的主要组件包括:
- Espresso - 用于与视图交互(通过
onView()
和onData()
)的入口点。此外,还公开不一定与任何视图相关联的 API,如pressBack()
。 - ViewMatchers - 实现
Matcher<? super View>
接口的对象的集合。我们可以将其中一个或多个对象传递给onView()
方法,用来查找特定视图。 - ViewActions - 可以传递给
ViewInteraction.perform()
方法的ViewAction
对象的集合,例如click()
。 - ViewAssertions - 可以通过
ViewInteraction.check()
方法传递的ViewAssertion
对象的集合。在大多数情况下,我们可以使用 matches 断言当前选中视图的状态。
下面来写个示例:
api 都很小巧,体会下,应该就可以上手了。
三板斧,查找视图 -> 对视图操作 -> 对视图断言。
简单测试
EspressoUiActivity 类中包含一个 Button 和一个 TextView。点击该按钮后,TextView 的内容会变为 "espresso"。
我们在 src/androidTest/java/ 路径下编写测试类 EspressoUiTest,注意 activityScenarioRule 不能忘:
好了,执行用例,发现用例通过:
AdapterView测试
AdapterView
可从适配器动态加载数据。最常见的 AdapterView
是 ListView。和 LinearLayout 不同的是,其有时只能将一部分 AdapterView
子视图加载到当前视图层次结构中。使用简单的 onView() 将找不到当前未加载的视图。
Espresso 为此提供一个单独的 api onData(),该 api 能够先加载相关适配器数据,并在对其或其任何子级执行操作之前使其处于聚焦状态。
来个示例:
EspressoUiActivity 类中包含一个 ListView 和一个 TextView。点击该item后,TextView 的内容会变为 "item$position"。
首先我们在测试类 EspressoUiTes 中使用 onData
,可以模拟用户操作:
RecyclerView 测试
关于 RecyclerView 的 UI 测试,我们需要额外引用依赖库:
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.5.0-alpha05'
来个示例吧,跟 AdapterView 使用类似:
EspressoUiActivity 类中包含一个 RecyclerView,代码见下。
这是适配器类 CustomAdapter
部分代码,就是取 middle' item 赋值 “middle”,其他 item 赋值 “item$position”:
我们在测试类 EspressoUiTest 使用 RecyclerViewActions 提供的接口,可以来模拟用户操作:
不复杂吧,不止如此,RecyclerViewActions 还提供了 api:
scrollTo()
- 滚动到匹配的视图。scrollToHolder()
- 滚动到匹配的数据视图持有者。scrollToPosition()
- 滚动到特定位置。actionOnHolderItem()
- 对匹配的视图持有者执行视图操作。actionOnItem()
- 对匹配的视图执行视图操作。actionOnItemAtPosition()
- 在特定位置对视图执行视图操作。
我们再来看个 scrollToHolder()
的示例,可以自定义个匹配器,在 matchesSafely
编写匹配条件(发散下,我们完全可以通过 Holder 做更多的匹配工作,甚至与外部传入的其他 Matcher 共同作用):
自定义Matcher
EspressoUiActivity 类中包含一个 EditTest,其 hint 属性为 “please input”。
现在编写一个 withHint
方法,将 BoundedMatcher 用作基匹配器,用来匹配 EditTest 的 hint。
现在就可以使用自定义的 withHint
接口了,即可以接收 String
类型,也可以接收 Matcher<String>
类型:
当然使用 TypeSafeMatcher
实现也没问题,大家灵活运用吧:
isDisplayed 和 doesNotExist()
isDisplayed 表示 view 被展示出来了,与 not(isDisplayed) 对应,使用起来很简单。
EspressoUiActivity 类中包含一个 Button,处于 Visible 状态,点击后置于 Gone / Invisible 状态。
我们在测试类 EspressoUiTest 中,可以使用 isDisplayed
检查:
doesNotExist 表示视图不见了,即相应视图从视图层次结构中消失了,不是隐藏。下面来个例子:
EspressoUiActivity 类中包含一个 Button,处于 Visible 状态,点击后从父布局中移除。
我们在测试类 EspressoUiTest 中,可以使用 doesNotExist
检查:
Espresso-Intents
Espresso-Intents 是 Espresso 的扩展程序,它能够帮助我们 从下面2个方面来验证 Intent
:
- 验证传出的
Intent
携带的数据 - 模拟
ActivityResult
使用 Espresso-Intents 需要引入依赖库:
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.5.0-alpha05'
下面分别介绍下,怎么编写测试用例:
send Intent
EspressoUiActivity 类中包含一个 Button。点击该按钮后,会跳转到另一个 Activity 界面。
我们可以在测试类 EspressoUiTest 中,使用 Intents.intended
接口:
ActivityResult
EspressoUiActivity 类中包含一个 Button 和 一个 TextView。点击 Button 后,会跳转到另一个界面 EspressoUiActivity2,返回时, TextView 的内容变为 ActivityResult 携带的结果。
我们在测试类 EspressoUiTest 中,可以使用 Intents.intending
接口,同样别忘了 Intents.init()
:
有一点要注意,使用了 ActivityResultLauncher,就不能直接 startActivity 了,可以使用 startForResult.launch。
多窗口
当一个界面存在多个窗口时,Espresso 可能会迷惑,不知道哪个是要交互的目标窗口,这个时候,就需要我们手动指定,例如:AutoCompleteTextView 这类自动补全的 带下拉菜单的 View。
来个示例吧:
EspressoUiActivity 类中包含一个 AutoCompleteTextView
,当输入 “Red” 时,下拉菜单会补全为 “Rea Sea” 可供点击。
我们可以在测试类 EspressoUiTest 中,我们需要 inRoot(withDecorView(..)) 指定窗口:
注意,并非所以窗口 Espresso 都识别不了。像 AlterDialog 这类,使用起来跟普通 view 一样简单。
来个示例吧:
EspressoUiActivity 类中包含一个 Button 和 一个 TextView,当点击 Buttton 后。会弹出 AlterDialog 展示 item 列表供点击,点击会在 TextView 显示。
我们可以在测试类 EspressoUiTest 中,直接使用 onData
api 即可:
好了,本节就到这里了,代码我都上传 gitHub 了。
本节完。