本页概述了测试 Android 应用的核心原则,包括主要的最佳实践及其优势。
测试的优势
测试是应用开发过程中不可或缺的一部分。通过对您的应用进行持续的测试,您可以在公开发布之前验证应用的正确性、功能行为和可用性。
您可以手动浏览应用来对其进行测试。您可以使用不同的设备和模拟器、更改系统语言,并尝试重现每一个用户错误或遍历每一个用户流程。
然而,手动测试难以扩展,并且很容易忽略应用行为中的回归问题。自动化测试涉及使用能够为您执行测试的工具,这种方式速度更快、可重复性更高,并且通常能在开发过程的早期为您提供更多可操作的反馈。
Android 中的测试类型
移动应用非常复杂,必须在多种环境中良好运行。因此,存在多种类型的测试。
测试主体
例如,根据测试主体的不同,测试类型也有所区别:
- 功能测试 (Functional testing):我的应用是否实现了预期功能?
- 性能测试 (Performance testing):它的运行速度是否快且高效?
- 无障碍测试 (Accessibility testing):它是否能与无障碍服务良好配合?
- 兼容性测试 (Compatibility testing):它是否能在所有设备和 API 级别上良好运行?
作用域
测试还可以根据规模或隔离程度进行划分:
- 单元测试 (Unit tests) 或 小型测试仅验证应用的一小部分,例如一个方法或类。
- 端到端测试 (End-to-end tests) 或 大型测试同时验证应用的较大组成部分,例如整个屏幕或用户流程。
- 中型测试 (Medium tests) 介于两者之间,用于检查两个或多个单元之间的集成 (integration) 情况。
测试的分类方式有很多。然而,对于应用开发者来说,最重要的区别在于测试运行的位置。
插桩测试与本地测试
您可以在 Android 设备上或另一台计算机上运行测试。
- 插桩测试 (Instrumented tests) 在 Android 设备(实体设备或模拟器)上运行。应用会与一个测试应用一起构建并安装,该测试应用会注入命令并读取状态。插桩测试通常是 UI 测试,即启动应用并与其进行交互。
- 本地测试 (Local tests) 在您的开发机器或服务器上执行,因此也称为宿主端测试 (host-side tests)。它们通常规模较小且运行速度快,能够将测试对象与应用的其余部分隔离开来。
并非所有的单元测试都是本地测试,也并非所有的端到端测试都在设备上运行。例如:
- 大型本地测试:您可以使用在本地运行的 Android 模拟器,例如 Robolectric。
- 小型插桩测试:您可以验证代码是否能与框架功能(例如 SQLite 数据库)良好配合。您可以在多台设备上运行此测试,以检查与不同 SQLite 版本的集成情况。
示例
以下代码片段展示了如何在插桩 UI 测试中与 UI 进行交互,该测试会点击一个元素并验证是否显示了另一个元素。
Espresso
// When the Continue button is clicked
onView(withText("Continue"))
.perform(click())
// Then the Welcome screen is displayed
onView(withText("Welcome"))
.check(matches(isDisplayed()))
Compose UI
// When the Continue button is clicked
composeTestRule.onNodeWithText("Continue").performClick()
// Then the Welcome screen is displayed
composeTestRule.onNodeWithText("Welcome").assertIsDisplayed()
此片段展示了 ViewModel 的单元测试(本地、宿主端测试)的一部分。
// Given an instance of MyViewModel
val viewModel = MyViewModel(myFakeDataRepository)
// When data is loaded
viewModel.loadData()
// Then it should be exposing data
assertTrue(viewModel.data != null)
可测试的架构
借助可测试的应用架构,代码结构允许您轻松地独立测试其不同部分。可测试的架构还具有其他优势,例如更好的可读性、可维护性、可扩展性和可重用性。
一种不可测试的架构会导致以下后果:
- 更大、更慢、更不稳定的测试。无法进行单元测试的类可能必须通过更大的集成测试或 UI 测试来覆盖。
- 测试不同场景的机会更少。大型测试速度较慢,因此测试应用的所有可能状态可能是不现实的。
如需了解有关架构指南的更多信息,请参阅应用架构指南。
解耦方法
如果您能将函数、类或模块的一部分从其余部分中提取出来,测试它就会变得更容易且更有效。这种做法被称为解耦,它是可测试架构中最重要的概念。
常见的解耦技术包括:
- 将应用拆分为层 (layers),例如表现层、领域层和数据层。您还可以将应用拆分为模块 (modules),每个功能一个模块。
- 避免在具有大量依赖项的实体(如 Activity 和 Fragment)中添加逻辑。将这些类用作框架的入口点,并将 UI 和业务逻辑移动到其他地方,例如 Composable、ViewModel 或领域层。
- 避免在包含业务逻辑的类中直接进行框架依赖 (framework dependencies)。例如,不要在 ViewModel 中使用 Android Context。
- 使依赖项易于替换。例如,使用接口而不是具体实现。即使不使用 DI 框架,也要使用依赖注入。
后续步骤
现在您已经了解了为什么要进行测试以及两种主要的测试类型,您可以阅读测试内容或了解测试策略。
或者,如果您想创建您的第一个测试并通过实践学习,请查看测试 Codelab。