第1章:覆盖率驱动方法论——TDD与CDD的区别
大家好,我是你们的老朋友。今天咱们聊聊测试驱动开发(TDD)和覆盖率驱动开发(CDD)的区别。这两个概念,我见过太多人搞混了。
先说说我的经历。几年前我带一个嵌入式项目,团队严格执行TDD。代码写得很漂亮,测试用例也很多。但上线前做覆盖率分析,发现核心模块的覆盖率只有60%。大家傻眼了——测试都通过了啊,怎么覆盖率这么低?
这就是TDD的盲区。你写了测试,但测了什么?测了多少?没人知道。说白了,TDD关注的是「测试先行」,而CDD关注的是「测全了没」。
1.1 TDD:先写测试,再写代码
TDD的核心流程,大家应该都熟悉:
- 写一个失败的测试用例
- 写刚好能让测试通过的代码
- 重构代码
这个循环很漂亮。但有个问题——你怎么知道测试用例覆盖了所有场景?
TDD的致命伤:测试通过 ≠ 代码没问题。你只是验证了你想到的场景,没想到的呢?
我记得有一次,团队用TDD开发一个状态机。测试全过了,但上线后有个边界条件没覆盖到,导致系统崩溃。事后分析,那个场景其实很简单,就是没人想到要测它。
1.2 CDD:覆盖率说了算
CDD的思路完全不同。它把覆盖率作为测试的「第一性原理」。流程是这样的:
- 先写代码(或者先有设计)
- 分析代码的覆盖率目标(比如行覆盖90%,分支覆盖85%)
- 写测试用例,目标是达到覆盖率目标
- 运行测试,分析覆盖率报告
- 补充测试,直到达标
你想想看,TDD是「先写测试再写代码」,CDD是「先定覆盖率目标再写测试」。方向完全不同。
我的建议:不要非此即彼。我习惯的做法是:核心逻辑用TDD保证正确性,然后用CDD保证完整性。两者互补,效果最好。
1.3 核心区别对比
咱们用一张表说清楚:
| 维度 | TDD | CDD |
|---|---|---|
| 驱动方式 | 测试用例驱动 | 覆盖率数据驱动 |
| 关注点 | 代码行为是否正确 | 代码是否被充分测试 |
| 测试时机 | 先写测试,后写代码 | 先定目标,后补测试 |
| 质量保证 | 依赖测试设计能力 | 依赖覆盖率分析工具 |
| 适用场景 | 新功能开发、算法实现 | 回归测试、安全关键系统 |
| 常见误区 | 测试通过就以为够了 | 盲目追求100%覆盖率 |
嗯,这里要注意。TDD和CDD不是对立的。我见过最好的团队,是把两者结合起来用。比如:
- 新功能用TDD开发,保证每个功能点都有测试
- 上线前用CDD做一轮覆盖率分析,查漏补缺
- 持续集成中同时跑TDD的单元测试和CDD的覆盖率检查
1.4 覆盖率驱动测试的核心流程
CDD的核心流程,我总结为四步:
- 定目标:根据项目风险等级,设定覆盖率目标。比如核心模块行覆盖≥90%,非核心模块≥70%
- 写测试:针对代码逻辑,设计测试用例。这里不是盲目写,而是「瞄准」未覆盖的代码
- 跑分析:用工具生成覆盖率报告。我常用gcov、JaCoCo、Istanbul这些
- 补测试:根据报告,补充遗漏的测试用例。循环直到达标
这个流程看起来简单,但坑很多。我曾经在一个项目中,团队把行覆盖率做到了95%,但分支覆盖率只有40%。为什么?因为大家只盯着「每行代码被执行了没」,却忽略了「每个分支条件都被测了没」。
避坑指南:我曾经犯过一个错误——只关注行覆盖率,忽略了分支覆盖率和条件覆盖率。结果一个if-else的分支没测到,线上出了严重bug。从那以后,我要求团队至少同时看行覆盖和分支覆盖两个指标。
1.5 覆盖率闭环管理
覆盖率不是测一次就完事的。它需要闭环管理。我画了一张图来说明:
闭环管理的关键,我总结为三点:
- 目标可量化:别说什么「尽量覆盖」,要说「行覆盖≥85%,分支覆盖≥80%」
- 过程可追溯:每次覆盖率报告都要存档,能回溯到具体版本
- 结果可闭环:发现覆盖率不足,必须补充测试,不能放过
我的小技巧:在CI/CD流水线中,我会设置覆盖率门禁。如果本次提交导致覆盖率下降超过1%,流水线直接失败。这样能倒逼开发人员每次提交都考虑测试覆盖。
1.6 什么时候用TDD,什么时候用CDD?
这个问题我经常被问到。我的回答是:
- 写新功能、新算法:用TDD。因为你需要先想清楚「这个功能应该怎么工作」
- 做回归测试、维护老代码:用CDD。因为代码已经存在,你需要知道「哪些代码还没被测到」
- 安全关键系统(汽车、医疗、航空):必须用CDD。因为标准要求覆盖率达标,比如ISO 26262要求MC/DC覆盖
我记得有一次给一个医疗设备项目做咨询。他们TDD做得很好,但客户审计时要求提供覆盖率报告。结果发现很多代码没覆盖到。后来我们引入CDD,花了两个月把覆盖率从65%提升到92%。客户很满意,审计也通过了。
所以,别纠结于「用哪个」。工具是死的,人是活的。根据场景选对工具,才是高手。
可可精品资料库