---
title: "在开发周期中应用自动化测试"
url: https://clean99.github.io/zh/2023/04/01/Applying-Automatic-Testing-in-The-Development-Cycle/
date: 2023.04.01
lang: zh
---

## 背景

测试是任何软件开发流程中的关键环节。确保应用程序按预期运行、没有缺陷，是向终端用户交付高质量产品的基本要求。本节将探讨测试在开发周期中的重要性，并重点介绍自动化测试流程的优势。

测试是生产流程不可或缺的一部分，务必牢记：**你的软件最终一定会被测试，无论是由你、你的自动化测试脚本，还是终端用户来完成**。疏于彻底测试产品，可能导致性能低下、出现错误，最终让客户不满。

大多数测试任务**简单且重复**，非常适合自动化。通过自动化测试流程，你可以节省宝贵的时间和资源、提升准确性，并**降低人为错误的可能性（始终牢记：人类并不完美，会犯很多低级错误）**。自动化测试还可以**按需频繁运行**，确保缺陷能被**及时发现和处理**。

在整个开发周期中，应当实施各种类型的测试，以便尽早发现 Bug 和问题，使其更易于解决。通过结合单元测试、端到端（E2E）测试等不同测试技术，可以确保应用程序在开发的每个阶段都得到全面验证。接下来的章节将探讨标准开发周期，并讨论如何在这一框架内有效地应用自动化测试。

## 开发周期

敏捷软件开发生命周期（SDLC）的标准工作流通常由几个阶段组成，每个阶段都有其目的和目标，包括：

<img alt="敏捷软件开发生命周期" src="/img/testing-sdlc/alige.png">

1. **构想**：此阶段涉及产生想法、识别机会，以及定义项目目标和需求。
2. **开发**：包括 UX/UI 设计、架构设计、编码和集成。
3. **测试**：此阶段涉及验证软件的功能、性能和整体质量。
4. **部署**：在此阶段，软件被逐步发布到生产环境。
5. **运维**：最后阶段涉及按需维护和更新软件、解决出现的问题，并根据用户反馈和性能指标持续改进产品。

不同公司可能有其独特的 SDLC 变体，以满足各自的需求和工作流程。在我的团队中，我们的 SDLC 遵循以下结构：

<img alt="我团队的软件开发生命周期" src="/img/testing-sdlc/sdlc.png">

1. **撰写 BRD/PRD**（业务需求文档/产品需求文档）：这些文档概述了项目的目标、宗旨和需求，是后续开发阶段的基础。
2. **PRD 评审**：团队成员（包括开发人员）审查 PRD，从用户和开发者的角度确保设计合理。
3. **撰写 TRD**（技术需求文档）：该文档概述了软件的技术规范和架构，详细说明如何实现产品需求。
4. **TRD 评审**：相关利益方评审技术需求，确保提出的解决方案与项目目标和约束条件一致。
5. **实现**（前端/后端）：开发人员开始编写应用程序代码，专注于前端和后端组件。
6. **代码评审**：其他团队成员审查代码，确保其遵循最佳实践并满足项目需求。
7. **重构**：在此阶段对代码进行必要的修改或改进。
8. **QA 测试**：进行质量保证测试，包括手动和自动化测试，确保软件达到预期的质量标准。
9. **小流量**：将应用程序逐步推送给一小部分用户，密切监控性能和用户反馈。
10. **全量流量**：软件最终部署给所有目标用户，标志着开发周期的完成。
11. **维护**：**世界上不存在完美的系统，需求也在不断变化**。因此我们需要频繁地维护和迭代我们的系统。

## 手动测试的局限性

尽管开发周期可以产出高质量的产品，但我的团队偶尔仍会遇到 Bug 和故障，其中一些由我自己负责。2023 年 3 月，我们经历了五次故障，虽然没有一次特别严重，但它们凸显了改进的必要性。**人为错误不可避免，这正是自动化不可或缺的原因。** 让我们来分析一下我团队故障中的一些具体问题。

### 代码变更与 QA 测试的交叉

<img alt="代码变更与 QA 测试的交叉" src="/img/testing-sdlc/interleaving-code-change.png">

理想情况下，**测试应始终跟随代码变更进行**。然而在实践中，这并不总是可行的。在 QA 测试阶段之后，开发人员还需要修复 Bug、解决合并代码时的冲突等。仅依靠手动测试，整个过程既耗时又消耗资源，反复进行全面 QA 测试变得不切实际。

以我团队故障中的这个例子为例：

1. **合并冲突导致路由缺失**：快速的产品迭代和同时进行的需求导致频繁的冲突解决。解决冲突可能引入 Bug，尤其是当处理冲突的人只编写了部分代码时。将手动测试聚焦于需求本身，使此类故障得以发生。

### 对黑盒模块的依赖

<img alt="依赖爆炸" src="/img/testing-sdlc/dependency.png">

我们的系统严重依赖众多外部依赖项。**模块化对于构建复杂系统至关重要，但这通常意味着依赖黑盒模块。** 信任这些依赖项可能导致 Bug 的产生。

以下是一些故障案例：

1. **依赖变更导致产品入口消失**：开发人员可能不了解其变更对下游产品的影响。当依赖项发生变化时，让 QA 手动测试所有下游产品是不切实际的。
2. **过时的 JS Bridge（JSB）文档导致某些 App 版本功能异常**：在开发使用 JSB 的新功能时，要求开发人员或 QA 测试每个 App 版本是不合理的。开发人员相信文档，但当文档过时时，故障就会发生。

### 组合爆炸

<img alt="组合爆炸" src="/img/testing-sdlc/combine.png">

我们的团队服务于全球各地区、语言、平台和 App 版本的用户。功能也有多种组合。**可能的组合数量 O(m^n)（A * B * C * D ...）是巨大的，且大多数是重复性的。** 人类并不擅长这项任务，如以下故障所示：

1. **某些地区缺少某个功能**：我们团队在多个地区发布了一个功能，但有一个月在某些地区缺失了该功能。简单的原因是没有人在这些地区进行测试。**期望 QA 为每个需求单独测试每个地区是不切实际的。**

## 在 SDLC 中应用自动化测试

提升软件开发敏捷性的关键之一是**自动化**。考虑到手动测试的局限性，我们知道**手动工作既耗时又容易出错**。因此，我们需要在 SDLC 中融入自动化测试。本节将介绍如何将各种类型的自动化测试应用于 SDLC，并展示它们在预防产品故障和 Bug 方面的有效性。

<img alt="含自动化测试的软件开发生命周期" src="/img/testing-sdlc/sdlc-testing.png">


### TRD：编写与评审测试用例

在 TRD 阶段，记录每个新模块的测试用例并让其他开发人员评审至关重要。**引入自动化测试后，测试用例提供了一种极佳的方式来描述用户与我们模块的交互。** 测试用例可能类似于："当用户点击某物时，应显示某内容"，或"输入某内容时，应返回某内容"。它们描述了用户（终端用户或开发人员）如何与模块交互，作为代码库中的文档，提升了可维护性。

### 开发：单元测试与 TDD

在开发过程中，我们为各个单元创建测试脚本，并努力通过每个测试用例。这些测试是我们模块的第一批"用户"。**编写单元测试有诸多好处，例如隔离问题、简化排查、测试边界情况，以及复用现有测试确保我们不会意外破坏已有功能。** 通过采用测试驱动开发（TDD），你可以从单元测试中获得更多收益。查看我关于 [How Testing Speeds Up Your Development](/zh/2022/08/18/How-Testing-Speeds-Up-Your-Development/) 和 [Testing Best Practice Tdd](/zh/2022/07/24/Testing-Best-Practice-Tdd/) 的博文了解更多信息。

### 集成与测试：E2E 测试

完成前端和后端开发后，我们可以启动端到端（E2E）测试。通过自动化手动测试，我们显著降低了 QA 测试的复杂性。此外，我们可以复用之前的测试用例进行回归测试。**这让开发人员能够自信地修改代码和解决冲突，因为 E2E 测试会跟随他们的变更，确保应用程序的整体完整性。**

### 巡检、压力测试、性能测试等

我们可以为生产环境设置 E2E 测试巡检，以**尽快**发现问题，而不是等待用户反馈。通过实施压力测试和性能测试，我们可以从多个角度评估产品质量，确保最终产品更加健壮和可靠。

## 结论

总而言之，将自动化测试融入开发周期可以在提升**可靠性、可维护性和可扩展性**的同时，显著缩短开发时间。通过实施这些测试方法，上述大多数故障都可以得到预防，从而形成更**敏捷**、更**高效**的 SDLC。将自动化测试作为开发流程的重要组成部分，最终带来更高质量的产品，以及对开发人员和终端用户都更顺畅的体验。
