测试应用的活动

活动充当应用中每个用户交互的容器,因此测试应用的活动在以下设备级事件期间的行为非常重要

  • 另一个应用(例如设备的电话应用)中断应用的活动。
  • 系统销毁并重新创建活动。
  • 用户将活动置于新的窗口环境中,例如画中画 (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
    }
}

将活动驱动到新的状态

要将活动驱动到不同的状态,例如 CREATEDSTARTED,请调用 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()
            }
        }
    }
}