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