{"name":"单元测试","id":"软件工程-软件设计-代码质量-软件测试-单元测试","content":"# 单元测试\n\n> 单元测试既**证明代码局部正确**，也**控制变化带来的系统性风险**。\n\n## 单元测试的第一性原理\n\n### 软件系统的三个不变事实\n\n1. **变化不可避免**：需求变化、人员流动、环境演进是常态\n2. **人必然会犯错**：复杂系统中不存在零缺陷工程\n3. **复杂度只增不减**：系统一旦上线，复杂度随时间单调上升\n\n## 单元测试的独特价值\n\n### 不可替代性矩阵\n\n| 能力 | 单元测试 | 集成测试 | E2E测试 |\n|-----|---------|---------|--------|\n| 快速反馈（分钟级） | ✅ | ❌ | ❌ |\n| 精确定位失败 | ✅ | ❌ | ❌ |\n| 成本稳定可预测 | ✅ | ❌ | ❌ |\n| 约束架构设计 | ✅ | ❌ | ❌ |\n| 为重构提供信心 | ✅ | ❌ | ❌ |\n\n### 不可替代的根本原因\n\n**1. 代码级细粒度**\n\n> 单元测试验证的是**最小行为单元**的正确性。只有在代码级别，才能精确控制每一个执行路径、验证每一次副作用变更、隔离任意依赖项。\n\n**2. 确定性来源**\n\n> 单元测试的确定性来自**受控的内部状态**，而非外部环境的真实协作。当外部系统被替身后，测试才能做到任意时间可重复、任意环境一致、任意顺序执行。\n\n**3. 与代码共生演进**\n\n> 单元测试随代码同步创建、随代码同步重构、随代码同步删除。\n\n### 与其他测试层的协同\n\n```\n┌─────────────────────────────────────────────────────┐\n│                     E2E 测试                         │\n│         验证完整业务链路与用户旅程                   │\n├─────────────────────────────────────────────────────┤\n│                     集成测试                         │\n│         验证组件边界与协作协议                      │\n├─────────────────────────────────────────────────────┤\n│                     单元测试                         │\n│         验证最小单元行为与设计契约            ▲     │\n└─────────────────────────────────────────────────────┘\n                              只有单元测试能到达的深度\n```\n\n> 单元测试不替代其他测试，其他测试也替代不了单元测试。\n\n## 单元测试的核心原则体系\n\n### AIR 原则（工程稳定性）\n\n* **Automation**：测试必须可自动执行\n* **Independent**：测试之间互不依赖\n* **Repeatable**：在任何环境中结果一致\n\n### FIRST 原则（反馈效率）\n\n* **Fast**：支撑高频执行\n* **Independent**：失败可定位\n* **Repeatable**：去环境依赖\n* **Self-validating**：无需人工判断\n* **Timely**：测试与代码同步产生\n\n> 原则不是规范，而是**工程决策的约束条件**。真正的工程智慧在于：在约束内找到最适合当前场景的方案，而非机械套用原则。\n\n## 单元测试的边界与粒度\n\n### 什么是”单元”\n\n单元并非语法层面的方法或类，而是：\n\n> **一个职责闭合、行为可预测、依赖可替换的最小行为单元**\n\n### 粒度选择原则\n\n* 行为级而非实现级\n* 一个测试只验证一个概念\n* 核心路径优先覆盖\n\n## 单元测试的复杂性来源\n\n1. **输入复杂性**：一切影响执行路径的因素\n2. **输出复杂性**：所有被修改的状态与副作用\n3. **依赖复杂性**：外部系统、时间、随机性\n\n应对复杂性的唯一方式是：\n\n> **控制变量，而不是增加断言。**\n\n## 测试代码的工程规范\n\n### 测试即文档\n\n* 构造（Given）\n* 执行（When）\n* 验证（Then）\n\n```java\nassertUserExists(\"cxk\");\n```\n\n好的测试 API 是业务语言的直接表达。\n\n### 断言策略\n\n* 断言最少化\n* 行为级断言优先\n* 避免实现细节绑定\n\n### 命名原则\n\n* 测试名表达行为\n* 细节放入注释\n\n## 可测性：架构质量的外显指标\n\n### 不可测的根因\n\n* 隐式依赖\n* 强耦合\n* 副作用泛滥\n\n### 可测性改造策略\n\n* **对象接缝**：继承替换行为\n* **接口接缝**：依赖抽象\n* **新生策略**：新增可测路径\n* **包装策略**：隔离历史代码\n\n> 重构不是为了测试，测试是为了暴露重构需求。\n\n## 覆盖率的工程语境\n\n### 覆盖率的本质\n\n覆盖率是**风险可见性指标**，而非质量指标。\n\n### 决策导向的覆盖策略\n\n| 场景   | 推荐覆盖    |\n| ---- | ------- |\n| 核心业务 | 分支 / 条件 |\n| 工具代码 | 行覆盖     |\n| 遗留系统 | 新增路径    |\n\n## 测试数据构造策略\n\n测试数据的构造方式决定了测试的**可控性与覆盖广度**：\n\n| 构造模式 | 适用场景 | 核心特征 |\n| ------- | -------- | -------- |\n| 精确构造 | 核心路径验证 | 已知输入、确定输出 |\n| 边界构造 | 异常路径覆盖 | 极值、null、空场景 |\n| 随机构造 | 鲁棒性验证 | 大量迭代、边界穿透 |\n| 替身构造 | 依赖隔离 | 内存替身、虚假实现 |\n\n**策略原则**：\n\n* 构造数据时应保持**测试意图清晰**\n* 同一测试内，构造复杂度应与被测职责复杂度匹配\n* 避免构造过度——测试本身不应成为被测对象\n\n## 测试策略的层级分布\n\n不同抽象层级的代码，其测试策略应由其**职责边界**决定：\n\n### 边界层（数据访问、基础设施）\n\n* **策略**：存储替身 + 事务边界控制\n* **关注**：资源管理正确性，而非数据本身\n* **原则**：不访问真实外部资源\n\n### 业务层（服务、领域逻辑）\n\n* **策略**：依赖注入 + Mock/Stub\n* **关注**：业务规则正确性\n* **原则**：隔离所有外部协作方\n\n### 纯函数层（工具类、算法）\n\n* **策略**：输入输出全集验证\n* **关注**：所有路径的确定性与边界行为\n* **原则**：无依赖、无副作用、全可测\n\n## 组织与文化视角\n\n### 单测失败的真实原因\n\n* 交付压力\n* 架构债务\n* 评价体系缺失\n\n### 单元测试的真正价值\n\n**单元测试是对\"代码会按预期运行\"这一承诺的显式固化。**\n\n系统的复杂性来自两个方面：状态空间的无穷膨胀，依赖关系的无限延伸。单元测试的价值，在于以**最小可控单元**为边界，在状态空间和依赖网络中锚定出一条**确定的行为路径**。\n\n这条路径的意义不在当下，而在未来：\n\n* 当有人修改代码，测试是**第一道防线**——快速反馈破坏点\n* 当系统复杂度突破人脑理解上限，测试是**系统的记忆**——保留对行为的共识\n* 当技术债务累积到必须重构，测试是**重构的合法性与信心来源**——证明改变未破坏承诺\n\n> 单元测试不证明代码正确，它证明的是**代码曾经正确过**，以及**在什么前提下正确**。\n\n这种对\"前提条件\"的显式表达，才是单元测试作为知识载体的核心价值。它告诉后来的维护者：这个函数在什么输入下、产生什么输出、依赖什么外部条件、修改什么内部状态。\n\n## 结语\n\n单元测试的实质，是将**行为假设转化为可验证事实**的工程化手段。\n\n它要求开发者：\n\n* 接受变化\n* 正视复杂\n* 用结构化手段对抗熵增\n\n## 关联内容（自动生成）\n\n- [/软件工程/软件设计/代码质量/软件测试/软件测试.md](/软件工程/软件设计/代码质量/软件测试/软件测试.md) 软件测试是单元测试的上层概念，定义了整个测试体系的分类和原则，单元测试是其中的重要组成部分\n- [/软件工程/软件设计/代码质量/软件测试/自动化测试.md](/软件工程/软件设计/代码质量/软件测试/自动化测试.md) 自动化测试与单元测试密切相关，单元测试是自动化测试的基础和核心组成部分\n- [/软件工程/软件设计/代码质量/整洁代码.md](/软件工程/软件设计/代码质量/整洁代码.md) 整洁代码与单元测试相互促进，可测试性是整洁代码的重要特征，单元测试是保证代码整洁性的安全网\n- [/软件工程/软件设计/代码质量/代码重构.md](/软件工程/软件设计/代码质量/代码重构.md) 单元测试是重构的安全网，确保在不改变外部行为的前提下调整内部结构，是重构实践的重要基础\n- [/软件工程/DevOps.md](/软件工程/DevOps.md) 单元测试是DevOps流水线中的重要环节，为持续集成和持续部署提供质量保障\n- [/软件工程/架构模式/分层架构.md](/软件工程/架构模式/分层架构.md) 分层架构提升了代码的可测试性，各层可以采用不同的测试策略，单元测试在其中扮演重要角色\n- [/软件工程/架构/演进式架构.md](/软件工程/架构/演进式架构.md) 演进式架构强调代码的可测试性，单元测试是支持架构演进的重要手段\n- [/运维/持续集成.md](/运维/持续集成.md) 持续集成流程中会自动运行单元测试，保证代码变更不会引入新的问题\n- [/软件工程/软件设计/代码质量/软件测试/接口测试.md](/软件工程/软件设计/代码质量/软件测试/接口测试.md) 接口测试验证组件边界的行为契约，与单元测试的最小单元契约形成层级互补，共同构成完整的行为验证体系\n- [/软件工程/质量工程.md](/软件工程/质量工程.md) 质量工程是测试体系的上层方法论，单元测试是其中预防缺陷、控制风险的核心手段","metadata":"tags: ['软件工程', '运维']","hasMoreCommit":false,"totalCommits":1,"commitList":[{"date":"2026-06-14T12:12:42+08:00","author":"MY","message":"refactor(responsive): 响应式合流 P0-P2 — 响应式断点 + 令牌统一 + 单一响应式外壳","hash":"7019457ca1b42a02edc0b155de936713ba81f526"}],"createTime":"2026-06-14T12:12:42+08:00"}