测试应用的 activity

activity 充当应用中每次用户互动的容器,因此 请务必测试应用的 activity 在设备级的行为方式 事件,例如:

  • 其他应用(如设备的电话应用)中断了您应用的 Activity。
  • 系统销毁又重新创建了您的 Activity。
  • 用户将 activity 置于新窗口环境中,例如 即画中画 (PIP) 或多窗口模式。

具体而言,务必要确保您的 activity 在 对活动 生命周期

本指南介绍如何评估应用维护数据的能力 并在应用的 activity 转换时提供良好的用户体验 其生命周期的不同状态。

推动 Activity 的状态转换

测试应用 activity 的一个关键方面是放置应用的 在特定状态下的活动。为了定义这个“给定的”测试时,请使用 ActivityScenario 的实例, 部分 AndroidX Test 库。使用此类,您可以 将您的 activity 置于模拟设备级事件的状态。

ActivityScenario 是可在本地单元测试中使用的跨平台 API 和设备端集成测试在真实设备或虚拟设备上 ActivityScenario 可提供线程安全,它会在您的 测试的插桩线程以及运行被测 activity 的线程。

该 API 特别适合用来评估 Activity 如何 测试在被销毁或创建时的行为。这一部分介绍了 与此 API 相关的用例。

创建 Activity

要创建被测 Activity,请添加以下代码段中所示的代码:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
       launchActivity<MyActivity>().use {
       }
    }
}

创建 activity 后,ActivityScenario 会将 activity 转换为 RESUMED 状态。此状态表示您的 activity 正在运行且 显示给用户。在此状态下,你可以自由与 activity 的 View 元素(使用 Espresso 界面测试)。

Google 建议您在测试时对该 activity 调用 close 。这样就能清理相关资源 测试的稳定性ActivityScenario 会实现 Closeable,因此您可以 应用 use 扩展,或 try-with-resources(在 Java 编程中) 以便 activity 自动关闭。

或者,您也可以使用 ActivityScenarioRule 自动调用 每次测试前 ActivityScenario.launchActivityScenario.close 。以下示例展示了如何定义规则并获取 场景示例:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @get:Rule var activityScenarioRule = activityScenarioRule<MyActivity>()

    @Test fun testEvent() {
        val scenario = activityScenarioRule.scenario
    }
}

使 Activity 转换到新的状态

如需推动 activity 进入其他状态(如 CREATEDSTARTED),请调用 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 或自行完成,如以下代码所示 snippet:

@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 需要 以便在插桩线程中运行它们。

如需在被测 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()
            }
        }
    }
}