测试策略

自动化测试以多种方式帮助您提高应用质量。例如,它帮助您执行验证、捕获回归并验证兼容性。一个好的测试策略让您可以利用自动化测试来关注一个重要的好处:开发人员生产力

当团队采用系统的测试方法并配合基础设施增强时,可以实现更高的生产力。这样做可以及时反馈代码的行为。一个好的测试策略会执行以下操作:

  • 尽早发现问题。
  • 执行速度快。
  • 当需要修复时提供明确的指示。

本页将帮助您决定要实施哪种类型的测试、在哪里运行以及运行频率。

测试金字塔

您可以根据大小对现代应用程序中的测试进行分类。小型测试只关注代码的一小部分,因此它们快速可靠。大型测试范围广泛,需要更复杂的设置,难以维护。然而,大型测试具有更高的保真度*,它们可以一次发现更多问题。

*保真度是指测试运行时环境与生产环境的相似程度。

The distribution of the number of tests by scope is typically visualized in a pyramid.
图 1. 按范围划分的测试数量分布通常以金字塔形式可视化。

大多数应用应该有许多小型测试和相对较少的大型测试。每个类别中测试的分布应形成一个金字塔,数量更多的小型测试构成底部,数量较少的大型测试构成顶部。

最小化 Bug 成本

一个好的测试策略会最大限度地提高开发人员生产力,同时最大限度地降低发现 Bug 的成本

考虑一个可能效率低下的策略示例。在这里,按大小划分的测试数量没有组织成金字塔。端到端大型测试过多,而组件 UI 测试过少。

A top-heavy strategy where a lot of the tests are performed manually and the device tests are only executed nightly.
图 2. 一种头重脚轻的策略,其中大量测试是手动执行的,并且设备测试仅在夜间执行。

这意味着在合并之前运行的测试太少。如果存在 Bug,测试可能无法在夜间或每周的端到端测试运行之前捕获它。

重要的是要考虑这对于识别和修复 Bug 的成本以及为什么将测试工作偏向于更小、更频繁的测试非常重要。

  • 当 Bug 被单元测试捕获时,通常在几分钟内修复,因此成本较低。
  • 端到端测试可能需要几天才能发现相同的 Bug。这可能会涉及多个团队成员,从而降低整体生产力并可能延迟发布。此 Bug 的成本更高。

话虽如此,低效的测试策略总比没有策略好。当 Bug 进入生产环境时,修复需要很长时间才能到达用户设备,有时甚至几周,因此反馈周期最长且成本最高。

可扩展的测试策略

测试金字塔传统上分为 3 类:

  • 单元测试
  • 集成测试
  • 端到端测试。

然而,这些概念没有精确的定义,因此团队可能希望以不同的方式定义其类别,例如使用 5 层:

A 5-layer test pyramid with the categories unit tests, component tests, feature tests, application tests, and release candidate tests, in ascending order.
图 3. 5 层测试金字塔。
  • 单元测试在主机上运行,并验证单个逻辑功能单元,不依赖于 Android 框架。
    • 示例:验证数学函数中的差一错误。
  • 组件测试验证模块或组件的功能或外观,独立于系统中的其他组件。与单元测试不同,组件测试的表面积扩展到单个方法和类之上的更高抽象级别。
  • 功能测试验证两个或多个独立组件或模块之间的交互。功能测试更大、更复杂,通常在功能级别操作。
  • 应用测试以可部署二进制文件的形式验证整个应用的功能。它们是大型集成测试,使用可调试的二进制文件(例如可以包含测试钩子的开发版本)作为被测系统。
    • 示例:可折叠设备中的 UI 行为测试以验证配置更改、本地化和无障碍功能测试
  • 发布候选版本测试验证发布版本的功能。它们类似于应用测试,不同之处在于应用程序二进制文件经过精简和优化。这些是在尽可能接近生产环境(不将应用暴露给公共用户帐户或公共后端)中运行的大型端到端集成测试。

此分类考虑了保真度、时间、范围和隔离级别。您可以在多个层中拥有不同类型的测试。例如,应用测试层可以包含行为测试、截屏测试和性能测试。

范围

网络访问

执行

构建类型

生命周期

单元

单个方法或类,依赖项最少。

本地

可调试

合并前

组件

模块或组件级别

多个类协同工作

本地
Robolectric
模拟器

可调试

合并前

功能

功能级别

与其他团队拥有的组件集成

模拟

本地
Robolectric
模拟器
设备

可调试

合并前

应用

应用级别

与其他团队拥有的功能和/或服务集成

模拟
预发布服务器
生产服务器

模拟器
设备

可调试

合并前
合并后

发布候选版本

应用级别

与其他团队拥有的功能和/或服务集成

生产服务器

模拟器
设备

精简发布版本

合并后
发布前

决定测试类别

根据经验法则,您应该考虑金字塔中可以为团队提供正确反馈级别的最低层。

例如,考虑如何测试此功能的实现:登录流程的 UI。根据您需要测试的内容,您将选择不同的类别:

被测对象

正在测试内容的描述

测试类别

测试类型示例

表单验证器逻辑

一个类,根据正则表达式验证电子邮件地址并检查是否输入了密码字段。它没有依赖项。

单元测试

本地 JVM 单元测试

登录表单 UI 行为

一个表单带有一个按钮,仅在表单已验证时启用

组件测试

运行在Robolectric上的UI 行为测试

登录表单 UI 外观

遵循 UX 规范的表单

组件测试

Compose 预览截屏测试

与身份验证管理器集成

将凭据发送到身份验证管理器并接收可能包含不同错误的响应的 UI。

功能测试

使用伪对象的 JVM 测试

登录对话框

当按下登录按钮时显示登录表单的屏幕。

应用测试

运行在Robolectric上的UI 行为测试

关键用户旅程:登录

使用测试帐户针对预发布服务器的完整登录流程

发布候选版本

运行在设备上的端到端 Compose UI 行为测试

在某些情况下,某些内容属于哪个类别可能带有主观性。测试向上或向下移动可能还有其他原因,例如基础设施成本、不稳定性以及测试时间过长。

请注意,测试类别并不决定测试类型,并且并非所有功能都必须在每个类别中进行测试。

手动测试也可以是您测试策略的一部分。通常,QA 团队执行发布候选版本测试,但他们也可以参与其他阶段。例如,对没有脚本的功能中的 Bug 进行探索性测试。

测试基础设施

测试策略必须由基础设施和工具支持,以帮助开发人员持续运行其测试并强制执行规则,确保所有测试都通过。

您可以根据范围对测试进行分类,以定义何时何地运行哪些测试。例如,遵循 5 层模型:

类别

环境(何地)

触发器(何时)

单元

[本地][4]

每次提交

组件

本地

每次提交

功能

本地和模拟器

合并前,在合并或提交更改之前

应用

本地、模拟器、1 部手机、1 部可折叠设备

合并后,在合并或提交更改之后

发布候选版本

8 部不同手机、1 部可折叠设备、1 部平板电脑

发布前

  • 单元组件测试在持续集成系统上为每个新提交运行,但仅针对受影响的模块。
  • 所有单元、组件功能测试在合并或提交更改之前运行。
  • 应用测试在合并后运行。
  • 发布候选版本测试每晚在手机、可折叠设备和平板电脑上运行。
  • 在发布之前,发布候选版本测试在大量设备上运行。

当测试数量影响生产力时,这些规则可能会随时间而改变。例如,如果您将测试移至夜间运行,您可能会减少 CI 构建和测试时间,但您也可能会延长反馈周期。