为什么该停止写单元测试了?
TL;DR
本文核心观点:
- ROI 倒转 — 手工单元测试在 AI 时代的投入产出比已成负数,维护成本持续攀升而覆盖率存在天花板
- 三层融合 — 属性测试覆盖不变量、AI 生成测试覆盖代码路径、运行时验证覆盖生产环境,形成完整覆盖链
- 角色升级 — 开发者从”写测试的人”变为”定义测试策略的人”,QA 从”执行测试”变为”设计验证体系”
- 历史跃迁 — AI 生成测试是继手工测试、自动化测试、TDD 之后的第四次跃迁,每次跃迁都让测试更高效
*“在 AI 工程化的多个早期案例里,团队尝试让 AI 主导生成测试而非要求开发者手工编写。个例观察显示:测试覆盖率和缺陷捕获率显著提升、开发速度加快;但具体的百分比因团队规模、原有测试成熟度与代码库类型差异很大。本节开头这种’一刀切数字’更多是为了构造修辞,不是可复制的工程结果。”
那个让开发者痛苦的传统
让我们先看一些业界反复出现的现象。
下面这些来自业内多份调查与公开报告的”反复观察到的趋势”——注意它们的具体百分比因调查口径、样本、年份差异很大,这里给出区间让读者大致感受:
- 多数开发者确实长期视写测试为负担,但具体比例在不同调查中差异极大,不能用单一数字下定论(参见 GitHub 2024 Octoverse 与 Stack Overflow Developer Survey 历年趋势)
- 写测试占用的开发时间因项目阶段(早期 vs 维护期)而变化,业界普遍报告在 20%-40% 区间
- 测试代码超过生产代码这一现象在持续集成要求高的项目里确实常见
- 测试维护成本在长期维护项目中通常占技术债务的一个可观比例
这些不是哪个公司独有的问题,是软件工程的结构性现象。但具体百分比请勿照搬——根据团队和阶段会有很大差异。
传统的单元测试模式假设:
- 开发者最了解自己的代码,所以应该由他们写测试
- 测试需要在代码编写时同步完成
- 测试覆盖率越高越好
- 测试代码应该像生产代码一样维护
但这些假设在AI时代正在崩塌。
反直觉的事实:当一个AI可以在几秒钟内生成比你更全面、更严谨的测试时,你还在手工写测试的意义是什么?
💡 Key Insight
当 AI 生成测试的成本接近零时,”因为一直这样做”不再是继续手工写测试的有效理由。
核心观点:测试的ROI正在变成负数
让我说一个可能让你不舒服的事实:传统单元测试的投入产出比,在AI时代可能已经是负数。
让我们算一笔账:
| 成本项 | 传统模式 | AI-Native模式 |
|---|---|---|
| 编写时间 | 30-40%开发时间 | AI生成:几分钟 |
| 维护成本 | 随代码变更持续投入 | AI自动更新 |
| 覆盖率天花板 | 人工编写,有遗漏 | AI可以逼近100% |
| 边缘情况 | 依赖开发者经验 | AI系统性地探索 |
传统测试的价值:
- 验证代码行为符合预期
- 提供回归保护
- 作为文档说明代码用法
传统测试的成本:
- 大量重复性工作
- 与代码变更的同步负担
- 质量依赖于编写者的经验
关键洞察:当AI可以更高质量、更低成本地完成同样的事时,手工写测试就变成了”仪式性工作”——我们这样做是因为”一直这样做”,而不是因为它是最优解。
这不是说测试不重要,是说传统的”手工写单元测试”方式可能过时了。
穿越周期:从手工验证到自动化到智能生成
让我们看看测试的演化史。
1970年代,手工测试:开发者写完代码,手工运行验证。可靠但低效,无法重复。
1990年代,自动化测试:xUnit框架兴起。测试可以自动运行,但还是要人工编写。效率提升,但编写成本仍在。
2000年代,TDD(测试驱动开发):先写测试,后写代码。理念先进,但实践中很少有团队能严格执行。
2010年代,测试自动化平台:Selenium、Appium等。测试可以自动执行,但编写和维护仍然昂贵。
2024年,AI生成测试:AI可以基于代码自动生成测试,基于变更自动更新测试。
| 时代 | 测试方式 | 编写者 | 维护成本 | 覆盖率 |
|---|---|---|---|---|
| 手工时代 | 人工运行 | 人 | 高 | 低 |
| 自动化时代 | 脚本自动运行 | 人 | 中 | 中 |
| TDD时代 | 先测试后代码 | 人 | 中 | 中 |
| 平台时代 | 专用工具 | 人 | 高 | 中高 |
| AI时代 | AI生成+自动维护 | AI | 低 | 高 |
历史在押韵:每一次测试技术的跃迁,都让测试变得更高效、更可靠。AI生成测试是下一次跃迁。
💡 Key Insight
历史在押韵:每一次测试技术的跃迁,都让测试变得更高效、更可靠。AI生成测试是下一次跃迁。
反直觉洞察:新测试范式的三层融合
停止写单元测试,不是停止测试,是用更好的方式做测试。
💡 Key Insight
停止写单元测试,不是停止测试,是用更好的方式做测试。
我提出一个新的测试范式:三层融合测试模型。
第一层:Property-based Testing(属性测试)
不是测试具体输入输出,而是测试”属性”——代码应该始终满足的条件。
传统测试
优势:
- AI可以基于代码自动生成属性
- 一次属性测试相当于数千个具体用例
- 发现边缘情况的能力远超人工
第二层:AI生成测试(AI-Generated Tests)
让AI基于代码自动生成测试用例。
AI可以:
- 分析代码路径,生成覆盖所有分支的测试
- 理解代码意图,生成验证意图的测试
- 基于变更,自动更新测试
- 从历史bug中学习,生成针对性测试
实施方式:
- 行为驱动:开发者描述期望行为,AI生成测试
- 契约驱动:定义API契约,AI验证契约遵守
- 覆盖率驱动:设定覆盖率目标,AI自动补足
第三层:Runtime Verification(运行时验证)
不是测试所有可能的情况,而是在运行时验证关键约束。
传统思维:在部署前尽可能多地测试 新思维:在生产环境中持续验证
运行时验证包括:
- 不变量检查:关键数据在运行时始终满足的条件
- 异常检测:基于统计模型检测异常行为
- 影子验证:新版本的并行验证
实战:从单元测试到三层融合
转型路线图
阶段一:引入AI测试生成(立即开始)
- 选择工具:GitHub Copilot、CodiumAI、Testim等
- 从最简单的模块开始
- 对比AI生成测试 vs 手工测试的质量
阶段二:减少手工单元测试(3-6个月)
- 停止要求”每个函数都要有单元测试”
- 将重点转移到集成测试和契约测试
- 只在核心逻辑保留少量手工测试
阶段三:建立三层融合体系(6-12个月)
- 属性测试:定义关键业务属性
- AI生成测试:覆盖日常代码变更
- 运行时验证:关键路径的不变量检查
新的测试金字塔
传统金字塔
传统的测试金字塔模型相信:底层是大量单元测试,中间是集成测试,顶端是少量的端到端测试。这个结构的隐含假设是——测试成本从上到下递增,所以应该用最多的单元测试覆盖底层逻辑。
但这个假设在 AI-Native 开发中已经不成立。AI 生成单元测试的成本已经接近零,这意味着金字塔的”底层优势”消失了。与此同时,金字塔的三个致命弱点反而被放大:
第一,不变量覆盖不足。 单元测试只验证具体输入输出,无法系统性地验证代码应该始终满足的不变量。属性测试(Property-based Testing)用一条属性代替数千个具体用例,是更高效的覆盖方式。
第二,代码路径盲区。 开发者编写单元测试时,天然倾向于测试”阳光路径”(happy path),忽略边缘情况和错误处理。AI 系统性地探索代码分支,弥补这一缺陷。
第三,维护成本累积。 传统金字塔要求每个代码变更都同步更新对应的单元测试。AI 自动更新测试的能力让这一成本结构发生了根本性改变——不是减少测试数量,而是降低维护成本。
三层融合模型不是推翻金字塔,而是重新定义了各层的职责:底层用属性测试覆盖不变量,中层用 AI 生成测试覆盖代码路径,顶层用运行时验证覆盖生产环境。三层各司其职,互相补足。
💡 Key Insight
停止写单元测试,不是停止测试,是用更好的方式做测试。
组织变革
三层融合模型的落地,不只是技术变革,更深层是角色的重新定义。
开发者的角色转变——从”写测试的人”变成”定义测试策略的人”。具体而言:
- 定义关键属性:哪些业务规则是不可违背的不变量?这需要 deep understanding of business,不是会写 assert 语句就能做到的
- 设计系统可测试性:如何在架构层面支持属性测试和运行时验证?这要求开发者在设计阶段就考虑验证体系
- 解释业务规则给 AI:prompt 写得清楚,AI 生成的测试才准确。开发者变成了 AI 和业务之间的翻译者
质量保障的角色转变——从”执行测试的人”变成”设计验证体系的人”。具体而言:
- 建立 AI 测试质量标准:AI 生成的测试质量良莠不齐,需要有人制定评判标准、验收流程
- 监控运行时验证效果:运行时验证的告警是不是真实的 bug?还是正常的业务波动?这需要持续的人工判断
- 持续优化三层融合体系:随着业务演进,三层之间的边界和覆盖范围需要定期重新评估
这个转变的核心是:从”测试执行”到”测试设计”的跃迁。手工写单元测试是执行层面的工作;定义属性、设计验证体系、制定 AI 测试标准是设计层面的工作。前者可以被 AI 替代,后者仍然需要人的判断。
💡 Key Insight
优雅的技术组织不是拥有最多单元测试的组织,而是拥有最有效验证体系的组织。
写在最后
我知道,”停止写单元测试”这个说法会冒犯很多人。
单元测试是软件工程的圣牛之一。质疑它,就像质疑敏捷、质疑设计模式一样。但请记住,每一个曾经的圣牛都曾是革命性的创新,也都终将被下一个创新取代。
在AI时代,我们需要问的不是”我们还应该写单元测试吗?”,而是”什么是最有效的质量保证方式?”
如果AI可以比我们更好地生成测试,如果我们宝贵的开发时间可以从重复性工作中解放出来,那为什么还要坚持旧的方式?
优雅的技术组织不是拥有最多单元测试的组织,而是拥有最有效验证体系的组织。
向死而生,不是悲观,是清醒。承认单元测试的时代可能正在过去,然后拥抱更智能的质量保障未来。
这就是AI-Native软件工程的智慧。
✅ 今天就能做的 5 件事
把”停止写单元测试”从一个激进命题变成你团队下周就能开始的迁移路径:
-
15 分钟内:识别你当前 PR 中最冗余的 1 个单元测试。 哪个测试你每次改它都要顺手改一遍 production code?哪个测试覆盖的是 AI 看一眼就能写的纯函数?不需要全面改革——先认出一处 ROI 为负的测试,后续清理就有起点。
-
1 小时内:让 AI 用你测试金字塔中最常见的功能模块生成 5 个测试。 选一个简单的工具函数(纯函数、明显的输入输出),用 Cursor / Copilot / Claude 生成单元测试。对比手工写的测试和 AI 写的测试——你会重新评估自己的时间花在哪里最有效。
-
本周内:识别 3 条不会过时的”业务不变量”。 比如”订单金额永远 ≥ 0”、”用户 VIP 等级只能升级不能降级”——这些不是测试用例,是规则。写下这些不变量(不是测试)比写 100 个 assert 更能保护业务。 它也是后续属性测试的素材。
-
2 周内:在新模块停止要求”100% 单测覆盖率”,改为”AI 生成覆盖率 + 关键属性测试”。 只在新写的代码上做实验——老代码维持现状。不要同时推翻所有东西。 评估标准是:bug 漏到生产环境的频率是否下降、而不是覆盖率数字。
-
1 个月内:建立一个”运行时验证”的最小回路。 哪怕只是在一个核心 API 上加一道断言检查:检测异常调用模式是否触发自动告警。部署前测试覆盖率的天花板,远不如生产环境的持续验证来得可靠。
延伸阅读
经典案例
- Dropbox的测试策略转变:从全覆盖到风险导向
- Google的测试哲学:大规模测试的实践
- Facebook的测试文化:Move Fast with Stable Infra
技术实现
- Property-based Testing: Hypothesis, QuickCheck
- AI Testing Tools: CodiumAI, Testim, Mabl
- Runtime Verification: Java assertions, Rust invariants
学术与理论
- 《Growing Object-Oriented Software, Guided by Tests》: TDD经典
- 《Unit Testing Principles, Practices, and Patterns》: 单元测试深入
- Formal Methods: 形式化验证方法
Published on 2025-04-25 深度阅读时间:约 12 分钟
AI-Native软件工程系列 #17 —— 探索AI时代的软件工程范式转移
💬 评论
💡 使用 GitHub 账号登录 即可参与讨论