Android 应用测试基础知识

本页概述了测试 Android 应用的核心原则,包括核心最佳实践及其优势。

测试的好处

测试是应用开发流程中不可或缺的一环。通过持续对应用运行测试,您可以在公开发布应用之前验证其正确性、功能行为和易用性。

您可以导航以手动测试应用。您可以使用不同的设备和模拟器,更改系统语言,并尝试生成每一次用户错误或遍历每个用户流。

但是,手动测试的扩缩能力很低,并且很容易忽略应用行为的回归问题。自动化测试涉及使用工具为您执行测试,这种测试速度更快、可重复性更高,并且通常可在开发过程的早期阶段为您提供有关应用的更切实可行的反馈。

Android 中的测试类型

移动应用非常复杂,必须在多种环境中顺畅运行。因此,测试有很多类型。

主题

例如,根据主题,有不同的测试类型:

  • 功能测试:我的应用是否按预期执行?
  • 性能测试:测试是否快速而高效?
  • 无障碍功能测试:它能否与无障碍服务搭配使用?
  • 兼容性测试:它是否适用于所有设备和 API 级别?

范围

测试还因大小隔离程度而异:

  • 单元测试小测试仅验证应用的一小部分,例如方法或类。
  • 端到端测试或大型测试可以同时验证应用的较大部分,例如整个屏幕或用户流。
  • 中型测试介于两个单元之间,用于检查两个或多个单元之间的集成。
可进行小型、中型或大型测试。
图 1:典型应用中的测试范围。

对测试进行分类的方法有很多种。不过,对应用开发者而言,最重要的区别在于测试的运行位置。

插桩测试与本地测试

您可以在 Android 设备或其他计算机上运行测试:

  • 插桩测试在 Android 设备上运行(无论是物理还是模拟)。该应用与注入命令和读取状态的测试应用一起构建和安装。插桩测试通常是界面测试,即启动应用然后与其进行交互。
  • 本地测试在开发机器或服务器上执行,因此也称为主机端测试。它们通常体型小且速度快,能够将受测对象与应用的其余部分隔离开来。
测试可以作为插桩测试在设备上运行,或在开发机器上作为本地测试运行。
图 2:根据运行位置的不同测试类型。

并非所有单元测试都是本地测试,也并非所有端到端测试都在设备上运行。例如:

  • 大型本地测试:您可以使用在本地运行的 Android 模拟器,如 Robolectric
  • 小型插桩测试:您可以验证代码是否与框架功能(例如 SQLite 数据库)很好地搭配使用。您可以在多台设备上运行此测试,以检查与多个版本的 SQLite 的集成。

示例

以下代码段演示了如何在点击某个元素并验证是否显示了另一个元素的插桩界面测试中与界面进行互动。

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)

定义测试策略

在理想情况下,您需要在应用兼容的每台设备上测试应用中的每一行代码。遗憾的是,由于这种方法速度缓慢且成本高昂,不实用。

优秀的测试策略能够在测试的保真度、速度和可靠性之间取得适当的平衡。测试环境与真实设备的相似度决定了测试的保真度。保真度测试会在模拟设备或实体设备上运行。可以在本地工作站的 JVM 上运行低保真度测试。高保真测试通常速度较慢且需要更多资源,因此并非每项测试都应该是高保真测试。

不稳定的测试

即使是在正确设计和实现的测试运行中,也会发生错误。例如,在真实设备上运行测试时,自动更新可能会在测试过程中开始,并导致测试失败。代码中的细微竞态条件可能只发生很小一部分。并非 100% 通过的测试会比较不稳定。

可测试的架构

使用可测试应用架构时,代码遵循的结构,可让您轻松地单独测试应用的不同部分。可测试架构还有其他优点,例如更好的可读性、可维护性、可伸缩性和可重用性。

不可测试的架构会产生以下影响:

  • 规模更大、速度更慢、更不稳定的测试。无法进行单元测试的类可能必须有更大规模的集成测试或界面测试。
  • 测试不同场景的机会更少。范围越大的测试速度越慢,因此测试应用的所有可能状态可能不现实。

如需详细了解架构准则,请参阅应用架构指南

分离方法

如果您可以从函数、类或模块的其余部分提取部分内容,那么测试会更简单、更有效。这种做法称为分离,这是对可测试架构最重要的概念。

常见的分离技术包括:

  • 将应用拆分为多个,例如呈现层、网域层和数据层。您还可以将应用拆分为多个模块,每个功能一个模块。
  • 避免向具有大型依赖项的实体(如 activity 和 fragment)添加逻辑。您可以将这些类用作框架的入口点,并将界面和业务逻辑移至其他位置,例如可组合项、ViewModel 或网域层。
  • 避免在包含业务逻辑的类中使用直接框架依赖项。例如,请勿在 ViewModel 中使用 Android 上下文
  • 使依赖项易于替换。例如,使用接口而非具体的实现。即使您不使用 DI 框架,也请使用依赖项注入

后续步骤

现在您已经了解了为什么应该进行测试以及两种主要的测试类型,您可以阅读要测试的内容了。

或者,如果您想创建首个测试并通过实际操作来学习,请查看测试 Codelab