Activity 是应用中每次用户交互的容器,因此测试应用的 Activity 在设备级事件(例如以下事件)期间的行为方式非常重要:
- 其他应用(例如设备的手机应用)中断了您的应用的 Activity。
- 系统销毁并重新创建了您的 Activity。
- 用户将您的 Activity 放在了新的窗口环境中,例如画中画 (PIP) 或多窗口。
具体而言,请务必确保您的 Activity 在响应 Activity 生命周期中描述的事件时行为正确。
本指南介绍了如何评估您的应用在 Activity 生命周期不同状态转换期间维护数据完整性和良好用户体验的能力。
驱动 Activity 状态
测试应用 Activity 的一个关键方面是,将应用的 Activity 置于特定状态。若要定义测试的此“给定”部分,请使用 ActivityScenario
实例(AndroidX Test 库的一部分)。使用此类,您可以将 Activity 置于模拟设备级事件的状态。
ActivityScenario
是一种跨平台 API,您可以在本地单元测试和设备上集成测试中使用它。在真实或虚拟设备上,ActivityScenario
提供线程安全,可同步测试的 Instrumentation 线程与运行受测 Activity 的线程之间的事件。
此 API 特别适合评估受测 Activity 在被销毁或创建时的行为。本部分介绍了与此 API 相关的最常见用例。
创建 Activity
要创建受测 Activity,请添加以下代码段中显示的代码:
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEvent() { launchActivity<MyActivity>().use { } } }
创建 Activity 后,ActivityScenario
会将 Activity 转换到 RESUMED
状态。此状态表示您的 Activity 正在运行并对用户可见。在此状态下,您可以自由地使用 Espresso UI 测试与 Activity 的 View
元素进行交互。
Google 建议您在测试完成后对 Activity 调用 close
。这会清理相关资源并提高测试的稳定性。ActivityScenario
实现了 Closeable
接口,因此您可以应用 use
扩展函数,或者在 Java 编程语言中使用 try-with-resources
,以便 Activity 自动关闭。
或者,您可以使用 ActivityScenarioRule
在每次测试前自动调用 ActivityScenario.launch
,并在测试拆卸时调用 ActivityScenario.close
。以下示例展示了如何定义规则并从中获取场景实例:
@RunWith(AndroidJUnit4::class) class MyTestSuite { @get:Rule var activityScenarioRule = activityScenarioRule<MyActivity>() @Test fun testEvent() { val scenario = activityScenarioRule.scenario } }
将 Activity 驱动到新状态
要将 Activity 驱动到不同状态(例如 CREATED
或 STARTED
),请调用 moveToState()
。此操作模拟了您的 Activity 分别因被其他应用或系统操作中断而停止或暂停的情况。
moveToState()
的用法示例如下所示:
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEvent() { launchActivity<MyActivity>().use { scenario -> scenario.moveToState(State.CREATED) } } }
确定当前 Activity 状态
要确定受测 Activity 的当前状态,请获取 ActivityScenario
对象中 state
字段的值。如果 Activity 重定向到另一个 Activity 或自行完成,检查受测 Activity 的状态尤其有用,如下面的代码片段所示:
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEvent() { launchActivity<MyActivity>().use { scenario -> scenario.onActivity { activity -> startActivity(Intent(activity, MyOtherActivity::class.java)) } val originalActivityState = scenario.state } } }
重新创建 Activity
当设备资源不足时,系统可能会销毁某个 Activity,这要求您的应用在用户返回应用时重新创建该 Activity。要模拟这些情况,请调用 recreate()
:
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEvent() { launchActivity<MyActivity>().use { scenario -> scenario.recreate() } } }
ActivityScenario
类会维护 Activity 保存的实例状态以及使用 @NonConfigurationInstance
注解的任何对象。这些对象会加载到受测 Activity 的新实例中。
检索 Activity 结果
要获取与已完成 Activity 相关联的结果代码或数据,请获取 ActivityScenario
对象中 result
字段的值,如下面的代码片段所示:
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testResult() { launchActivity<MyActivity>().use { onView(withId(R.id.finish_button)).perform(click()) // Activity under test is now finished. val resultCode = scenario.result.resultCode val resultData = scenario.result.resultData } } }
在 Activity 中触发操作
ActivityScenario
中的所有方法都是阻塞调用,因此 API 要求您在 Instrumentation 线程中运行它们。
要在受测 Activity 中触发操作,请使用 Espresso 视图匹配器与视图中的元素交互:
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEvent() { launchActivity<MyActivity>().use { onView(withId(R.id.refresh)).perform(click()) } } }
但是,如果您需要在 Activity 本身中调用方法,可以通过实现 ActivityAction
安全地执行此操作:
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEvent() { launchActivity<MyActivity>().use { scenario -> scenario.onActivity { activity -> activity.handleSwipeToRefresh() } } } }