我开始相信单元测试的那一天
工程师们在讨论单元测试何时真正值得,比较的是对小“单元”代码进行快速、隔离检查,还是更慢但更贴近真实场景的集成与端到端测试。许多人赞赏单元测试能捕捉细微逻辑 bug、支持重构并记录预期行为,但也批评那些脆弱、过度使用 mock 的测试会把实现固化下来,而且几乎发现不了真实缺陷。反复出现的主题是取舍:测试速度与真实度、测试维护成本与代码灵活性,以及到底应该根据风险、领域复杂度和团队工程实践水平投入多少测试。
“单元测试”的定义与范围
- 对“单元测试”到底是什么意思,存在强烈分歧。
- 一派引用经典定义:非常快的测试,避免数据库、文件系统、网络以及特殊环境设置。
- 也有人按职责来定义:
- 单元:单一职责,隔离的逻辑。
- 集成:带相关依赖的垂直切片。
- 端到端:完整的用户旅程。
- 也有人认为这些标签在实践中几乎没有意义;“单元”和“集成”测试看起来常常一样。
- 另一种观点:单元 = 独立于其他测试运行的测试(不受共享状态干扰)。
单元测试的感知收益
- 快速反馈循环;编码时可以持续运行。
- 能捕捉细微的边界情况和回归问题,尤其是在复杂逻辑、纯函数或动态语言中。
- 促进更好的设计:更小、更解耦的模块,显式依赖,更少副作用。
- 作为可执行的文档和规格说明,并在重构时充当护栏。
- 适合把已知 bug 和边界条件编码成永久检查。
- 有些人表示,在编写测试时发现非显而易见的 bug,会带来重大的“顿悟”时刻。
批评与常见失败模式
- 许多企业里的测试与实现和 mock 紧密绑定,使重构痛苦且脆弱。
- 高覆盖率可能掩盖低价值:测试往往只是“验证昨天的行为”,或者只是把代码重新实现一遍。
- 严格的覆盖率目标会鼓励无意义的测试和对指标的投机。
- 大型测试套件会变慢(十几分钟到几十分钟),让开发者不愿意运行它们。
- 维护不佳的测试会把糟糕的设计固化下来,在它们阻碍“速度”时被删除,并侵蚀信任。
集成、E2E,以及真实感 vs 速度
- 围绕“测试金字塔”存在争论:有人认为集成/E2E 测试提供了更多现实世界价值,能捕捉单元测试永远不会发现的 UI、布局、配置和跨服务问题。
- 也有人强调集成/E2E 测试成本更高、易不稳定、维护负担更重;建议数量更少且更有针对性。
- 容器和内存适配器之类的工具模糊了界限:集成测试仍然可以相对较快。
- 一些评论者更倾向于测试“连贯功能单元”(通常是跨多个类的切片),而不是单个类。
其他技术与元层面观点
- 静态类型能减少某些 bug 类别,但不能取代测试;讨论了更具表现力的类型与测试之间的权衡。
- 基于性质的测试被强调为发现意外边界情况和规格不一致的强大工具。
- 测试只是代码评审、人工 QA,以及在安全关键系统中大量人工验证和正式流程中的一层。