现代软件应用由众多动态组件构成,这些组件会同时生成、收集并相互拉取数据。一旦其中任一组件出现异常行为,甚至发生故障,就可能对其依赖的其他组件产生连锁反应。
依据软件本身的性质,这类错误或故障可能导致系统停机、经济损失、基础设施崩溃、安全隐患,甚至危及生命。因此我们需要对软件进行测试与监控。沿着软件生命周期在恰当的阶段、采用合适的方法与用例进行测试,能更早、更高概率地在用户之前发现问题。
When 与 Where 进行测试
一般而言,测试发生在软件开发生命周期(SDLC)中的测试阶段。但对于某些测试类型,并非如此——不同测试的实施与运行时机会有差异。在讨论如何选择合适的测试之前,我们先快速回顾一下不同测试类型的适用时机与位置。
常见测试类型
下表为工程化测试矩阵,按测试类型、识别问题、所处 SDLC 阶段与实施方式进行分层梳理,可作为选型与阶段规划的 “总览图” 与协同基准。
| 测试类型 | 识别问题 | 所处阶段(SDLC) | 实施方式 |
|---|---|---|---|
| 单元 | 意外或缺失的函数输入与输出 | 开发、测试 | 在代码中定义,通常由语言库提供 |
| API 与集成 | 与第三方服务的集成问题 | 开发、部署、测试 | 在代码中定义,通常结合语言与相关集成库 |
| UI | 用户界面中的功能交互 | 测试 | 专用测试框架 |
| 安全 | 漏洞与攻击向量 | 开发、测试、部署、运维 | 专用测试框架 |
| 性能 | 关键应用指标 | 部署、运维 | 取决于指标的工具 |
| 冒烟 | 构建后应用是否仍能工作 | 测试、部署 | 专用测试框架 |
| 回归 | 新代码是否破坏既有功能 | 测试、部署 | 专用测试框架 |
如何使用测试矩阵
使用矩阵做测试决策时,优先考虑以下维度:风险、反馈速度、变更范围、环境约束与成本、团队所有权。
- 业务与用户风险:影响面越广、频率越高,优先级越高(UI 关键路径、回归集合优先)。
- 变更类型:纯业务逻辑→单元;跨边界集成→API/集成;可视交互→UI。
- 反馈速度 vs 覆盖面:单元最快、成本最低;回归最慢、覆盖最广,按风险分层执行。
- 环境与数据依赖:能模拟就模拟,必要时才使用真环境与真实数据,控制不稳定因素。
- 所有权与协作:开发主导单元/契约,测试主导 UI/回归,平台/安全负责扫描与门禁。
按类型的触发器与执行要点:
- 单元测试:新增或重构函数/类;修复过的缺陷用例固化;边界条件与异常分支;纯算法与数据转换逻辑。
- API 与集成测试:接口/认证变更;第三方版本升级;限流/超时/重试策略;契约(Pact 等)作为合并门禁。
- UI 测试:登录、搜索、下单等关键用户路径;多端一致性;高易碎交互(拖拽、虚拟列表、复杂表单)。
- 安全测试:新增依赖或配置变更;外部暴露面扩大;CI 定期依赖与镜像扫描,发布前高危阻断。
- 性能测试:新功能触达热点路径;QPS/延迟/错误率目标;容量评估与基线回归;关键事务的 SLO 校验。
- 冒烟测试:每次构建与部署前后;服务与核心能力可用性检查;失败立即回滚或阻断后续流程。
- 回归测试:版本发版前;高风险合并;跨模块改动;按 “核心路径→高风险场景→长尾用例” 分层执行。
执行原则:左移优先、分层防护、契约优先于端到端、金丝雀 + 冒烟、监控闭环到问题复盘。
示例应用
我有一个正在从副项目逐步成长为完整应用的想法:一个待办聚合器。它会从多个外部服务拉取分配给我的任务,并将其汇总到一个更易浏览的列表中。应用通过各服务的 API 获取任务数据,用户可以对列表进行排序与筛选,并点击列表项查看任务详情。应用基于 TypeScript 与 React 开发,使用了 Material UI;同时还通过 React Native 提供了移动端与桌面端版本。
必备测试
除非有充分理由不纳入,本节所述测试通常都应该包含在应用的测试套件中。
单元测试
几乎所有应用都需要单元测试,且可以在编写代码时同步创建。只要应用包含一个以上具备功能性的组件,就应当配套单元测试。示例应用中有一个组件负责将 API 返回的数据转换为可在 UI 中渲染的 React 对象。
由于应用使用 TypeScript,可用于编写单元测试的选择很多,包括 Jest、Mocha 与 Jasmine。它们各有优劣,没有绝对的完美答案。Jest 目前可能更受欢迎,由 Facebook 创建用于测试 React。示例应用基于 React,自然契合。
API 与集成测试
示例应用高度依赖多个 API,这些 API 存在多种潜在故障点,处理不当就可能导致应用不可用。API 测试与集成测试并不完全相同:API 测试只关注 API 交互;集成测试可以包含 API 测试,也可能覆盖其他第三方集成,如外部组件。由于本例唯一的第三方集成是 API,我们可以将二者等同处理。
API 测试通常在应用代码之外执行,使用外部工具或在 CI 流水线中进行。开源选择包括自行编写调用 API 端点的测试、SoapUI(由制定 API 规范标准的团队出品)、Pact 与 Dredd。就我个人而言,常在 CI 中使用 Dredd,但 API 测试并没有唯一的正确选择。
UI 测试
凡是有可视化前端的应用,都需要自动化 UI 测试。此类测试通常会模拟界面交互,以验证其按预期工作。示例应用的 UI 简洁但对用户体验至关重要。
UI 自动化测试通常手动运行,或集成到 CI 流程中。幸运的是,成熟工具非常多,有的独立于编程语言,有的与语言生态绑定。如果你的应用是 Web 应用,这些工具通常使用无头浏览器在不可见的环境中执行测试;若是某种原生应用,则可选工具会有所不同。
可选测试
本节介绍在资源允许的情况下可以纳入的测试类型,它们有助于提升应用的稳定性与整体用户体验。
安全
安全问题比以往任何时候都更值得重视。你需要在开发阶段检查潜在的脆弱代码,同时关注依赖引入的新型供应链安全风险。除了测试之外,基于供应链管理需要对外部包进行生成清单与维护管理的需求正在快速增长,相关的合规监管也可能很快到来。用于满足上述需求的工具大致分为两类:有的同时扫描你的自有代码与外部依赖,有的只负责其中之一。
漏洞扫描是众多 SaaS 厂商发力的新增长点,但也有一些流行的开源或免费选择,例如 GitHub、Falco 与 Trivy。它们与编程语言无关,如何选择主要取决于你应用背后的基础设施。示例应用在用户本地设备运行,因此最合适的时机是在 CI/CD 构建过程中执行漏洞检查。
性能测试
如果没有任何性能监控,再精巧的应用也可能在用户手中 “掉链子”。与列表中多数在 SDLC 某些阶段性运行的测试不同,性能测试通常是持续进行的。一些工具支持以模拟负载的方式仿真生产使用场景,但这仍与真实用户不同。
由于性能监控通常需要集中化服务来汇聚与分析数据,相关工具多为商业服务。但也有一些开源或免费选择,例如 k6(用于模拟)、将 React <Profiler> 数据发送至类似 Grafana 的系统,以及 Lighthouse CI。
冒烟测试
许多测试方法关注的是单个功能或组件,而不是覆盖用户实际使用时的关键路径。冒烟测试通常在 QA 环境中验证新构建的关键功能是否可用,然后再进入后续测试。冒烟测试可以由 QA 团队手动执行,也可以使用自动化工具。选择何种工具取决于你要测试的目标,本文提到的许多工具都能提供帮助。
回归测试
回归测试并非具体的一组工具,而是一种将其他测试进行分组的最佳实践,目的是确保新功能不会对应用产生负面影响。比如,一个新版本增加了在聚合应用中修改任务状态并回写至来源任务的能力。下面这些测试将协同工作,以确保这个新功能不会影响既有的 “仅用于查看聚合任务” 的功能。
落地路径:从 0 到 1 到持续优化
- 第 1 步(基线化):建立最小可用集合——关键模块单元测试、核心 API 契约测试、部署冒烟清单。
- 第 2 步(CI 左移):PR 必跑单元 + 契约;主干集成后自动冒烟;高危变更自动加严门禁。
- 第 3 步(回归分层):按风险分层(核心路径→高风险→长尾),白天跑短集,夜间/预发跑全量。
- 第 4 步(可观测性):度量延迟/错误率/吞吐;测试内埋点与追踪关联,问题可追溯。
- 第 5 步(持续治理):定义测试健康度(稳定性、去冗、覆盖有效性);清理脆弱与重复用例;建立 “测试债务” 看板。
结语
本文梳理了应用可以实施的多种测试类型,以及它们能够预防的问题。这些问题可能损害用户体验、暴露安全风险,甚至导致用户不再愿意使用你的应用或服务。每当你新增功能或对现有功能做出重大变更时,都应当编写相应测试并尽可能频繁地运行。在许多现代 SDLC 流程中,测试通常会在开发者将代码提交到版本控制时自动运行,而你也应该保持高频提交的节奏。