自动化测试可通过多种方式帮助您提高应用质量。例如,它可帮助您执行验证、捕获回归问题和验证兼容性。通过良好的测试策略,您可以利用自动化测试来专注于一项重要优势:提高开发者的工作效率。
如果团队结合使用系统化方法和基础架构增强功能,可以实现更高的工作效率。这样可以及时提供有关代码行为的反馈。良好的测试策略具有以下特点:
- 尽早发现问题。
- 执行速度快。
- 在需要修正问题时提供明确指示。
本页将帮助您确定要实现哪些类型的测试、在哪里运行这些测试以及运行频率。
测试金字塔
您可以按大小对现代应用中的测试进行分类。小型测试仅关注一小部分代码,因此速度快且可靠。大型测试的范围很广,需要更复杂的设置,并且难以维护。但是,大型测试具有更高的保真度*,并且一次性可以发现更多问题。
*保真度是指测试运行时环境与生产环境的相似程度。
大多数应用都应包含许多小型测试,而大型测试相对较少。每类测试的分配应该形成一个金字塔,数量较多的测试构成底部,数量较少的测试构成顶部。
尽可能降低 bug 的成本
良好的测试策略可以最大限度地提高开发者的工作效率,同时最大限度地减少发现错误的成本。
下面是一个可能效率不高的策略示例。在这里,按大小划分的测试次数没有整理到一个金字塔中。大型端到端测试过多,而组件界面测试过少:
这意味着,在合并之前运行的测试太少。如果存在 bug,测试可能不会在运行每夜或每周端到端测试之前发现。
请务必考虑这对发现和修复 bug 的成本的影响,以及为何应将测试工作重点放在更小且更频繁的测试上:
- 当单元测试捕获 bug 时,通常只需几分钟即可修复,因此成本较低。
- 端到端测试可能需要几天时间才能发现相同的 bug。这可能会吸引多个团队成员参与,降低整体工作效率,并可能延迟发布。此错误的成本较高。
不过,效果不佳的测试策略总比没有策略要好。如果 bug 进入了生产环境,修复程序需要很长时间才能到达用户的设备(有时需要数周时间),因此反馈环最长且成本最高。
可扩展的测试策略
传统上,测试金字塔分为 3 类:
- 单元测试
- 集成测试
- 端到端测试。
不过,这些概念没有确切的定义,因此团队可能希望以不同的方式定义其类别,例如使用 5 层:
- 单元测试在宿主机上运行,用于验证逻辑的单个功能单元,且不依赖于 Android 框架。
- 示例:验证数学函数中的差一错误。
- 组件测试用于独立于系统中的其他组件来验证模块或组件的功能或外观。与单元测试不同,组件测试的覆盖范围会延伸到单个方法和类之上的更高抽象层面。
- 示例:自定义按钮的屏幕截图测试
- 功能测试可验证两个或多个独立组件或模块的交互情况。功能测试更大、更复杂,通常在功能级别运行。
- 示例:用于验证屏幕中状态管理的界面行为测试
- 应用测试以可部署的二进制文件的形式验证整个应用的功能。它们是大型集成测试,使用可调试二进制文件(例如可能包含测试钩子的开发 build)作为受测系统。
- 示例:用于验证可折叠设备、本地化和无障碍功能测试中的配置更改的界面行为测试
- 候选版本测试用于验证发布 build 的功能。它们与应用测试类似,但应用二进制文件经过了缩减和优化。这些是大型端到端集成测试,在尽可能接近生产环境的环境中运行,而不会将应用公开给公共用户账号或公共后端。
- 示例:关键用户历程、性能测试
这种分类考虑了保真度、时间、范围和隔离级别。您可以在多个层中进行不同类型的测试。例如,“应用测试层”可以包含行为、屏幕截图和性能测试。
范围 |
网络访问 |
执行 |
build 类型 |
生命周期 |
|
---|---|---|---|---|---|
单位 |
依赖项最少的单个方法或类。 |
否 |
本地 |
可调试 |
合并前 |
组件 |
模块级或组件级 合并多个类 |
否 |
本地 |
可调试 |
合并前 |
功能 |
功能级别 与其他团队拥有的组件集成 |
模拟 |
本地 |
可调试 |
合并前 |
申请 |
应用级别 与其他团队拥有的功能和/或服务集成 |
模拟的 |
模拟器 |
可调试 |
合并前 |
候选版本 |
应用级别 与其他团队拥有的功能和/或服务集成 |
生产服务器 |
模拟器 |
缩减后的发布 build |
合并后 |
确定测试类别
一般来讲,您应该考虑能够为团队提供适当反馈的金字塔最低层级。
例如,考虑如何测试此功能的实现:登录流程的界面。您可以根据测试内容选择不同的类别:
被测对象 |
测试内容说明 |
测试类别 |
测试类型示例 |
---|---|---|---|
表单验证器逻辑 |
一个类,用于根据正则表达式验证电子邮件地址,并检查是否已输入密码字段。它没有任何依赖项。 |
单元测试 |
|
登录表单界面行为 |
带有仅在表单通过验证后启用的按钮的表单 |
组件测试 |
在 Robolectric 上运行的界面行为测试 |
登录表单界面外观 |
遵循用户体验规范的表单 |
组件测试 |
|
与身份验证管理器集成 |
向身份验证管理器发送凭据并接收可能包含不同错误的响应的界面。 |
功能测试 |
|
登录对话框 |
按下登录按钮时显示登录表单的界面。 |
应用测试 |
在 Robolectric 上运行的界面行为测试 |
关键用户历程:登录 |
使用测试账号对预演服务器执行的完整登录流程 |
候选版 |
在设备上运行的端到端 Compose 界面行为测试 |
在某些情况下,某项内容是否属于某个类别可能具有主观性。测试的优先级可能会因其他原因而上移或下移,例如基础架构费用、不稳定性和测试时间过长。
请注意,测试类别并不决定测试类型,并且并非所有功能都必须在每个类别中进行测试。
手动测试也可以成为测试策略的一部分。通常,QA 团队会执行候选版本测试,但他们也可以在其他阶段参与。例如,在不使用脚本的情况下,对功能进行探索性测试以查找 bug。
测试基础架构
测试策略必须由基础架构和工具支持,以帮助开发者持续运行测试并强制执行规则,确保所有测试都能通过。
您可以按范围对测试进行分类,以定义何时何地运行哪些测试。例如,以下 5 层模型:
类别 |
环境(where) |
触发器(何时) |
---|---|---|
单位 |
[Local][4] |
每次提交 |
组件 |
本地 |
每次提交 |
功能 |
本地和模拟器 |
合并前(在合并或提交更改之前) |
申请 |
本地、模拟器、1 部手机、1 部可折叠设备 |
合并后、合并或提交更改后 |
候选版本 |
8 部不同的手机、1 部可折叠设备、1 部平板电脑 |
发行前 |
- 系统会针对每个新提交在持续集成系统上运行单元和组件测试,但仅针对受影响的模块。
- 在合并或提交更改之前,系统会运行所有单元测试、组件测试和功能测试。
- 应用测试会在合并后运行。
- 系统会在手机、可折叠设备和平板电脑上每夜运行候选版本测试。
- 在发布之前,系统会在大量设备上运行候选版本测试。
当测试数量会影响工作效率时,这些规则可能会随着时间而改变。例如,如果您将测试改为每夜运行,可能会缩短 CI 构建和测试时间,但也可能会延长反馈周期。