
BDD 与 TDD 是什么?先把概念讲清楚
在软件开发里,很多团队都会说“我们要提升代码质量”“我们要减少返工”“我们要让需求更清楚”。但真正落到实践时,常见问题是:需求说不清、测试补得晚、代码写完才发现方向错了。BDD 和 TDD 就是为了解决这些问题而出现的两套开发方法。
TDD 是 Test-Driven Development,中文通常叫“测试驱动开发”。它强调在写功能代码之前,先写测试用例。开发流程通常是:先写一个失败的测试,再写刚好能让测试通过的代码,最后重构代码。它的关注点更偏向“代码是否按预期工作”。
BDD 是 Behavior-Driven Development,中文通常叫“行为驱动开发”。它是在 TDD 思想基础上发展出来的方法,更强调从用户行为、业务场景和产品目标出发描述系统应该如何表现。它的关注点更偏向“系统行为是否符合业务预期”。
简单来说:TDD 更像是开发者问自己:“这段代码应该怎么被验证?”BDD 更像是产品、测试、开发一起问:“用户在这个场景下应该得到什么结果?”
TDD 的核心流程:红、绿、重构
TDD 最经典的节奏是 Red、Green、Refactor,也就是“红、绿、重构”。
- Red:先写一个失败的测试。因为功能还没实现,所以测试应该失败。
- Green:写最少的代码让测试通过,不追求一步到位。
- Refactor:在测试保护下优化代码结构,保持行为不变。
这个流程的价值在于,它强迫开发者先思考“完成的标准是什么”。很多代码质量问题并不是因为开发能力不够,而是因为一开始没有定义清楚“什么叫正确”。TDD 把这个标准提前了。
一个简单的 TDD 示例
假设我们要写一个计算折扣价的方法。不要一上来就写实现,而是先写测试。
describe('calculateDiscountPrice', () => {
it('should return 80 when price is 100 and discount is 20%', () => {
expect(calculateDiscountPrice(100, 0.2)).toBe(80);
});
});
测试失败后,再写最小实现:
function calculateDiscountPrice(price, discount) {
return price * (1 - discount);
}
当测试通过后,再继续补充边界情况,例如折扣为 0、折扣不能超过 100%、价格不能为负数等。这样测试不只是验证结果,也会倒逼你设计更稳定的函数接口。
BDD 的核心:用业务语言描述行为
BDD 通常使用 Given、When、Then 的形式描述场景。它不是单纯给开发看的,而是让产品、测试、开发都能看懂。
- Given:给定一个前置条件
- When:当用户或系统执行某个动作
- Then:那么应该看到什么结果
一个 BDD 场景示例
Feature: 用户使用优惠券下单
Scenario: 用户拥有一张可用优惠券
Given 用户的购物车中有一件 100 元的商品
And 用户拥有一张满 50 减 20 的优惠券
When 用户提交订单
Then 订单应付金额应该是 80 元
And 这张优惠券应该被标记为已使用
这段描述的好处是:不懂代码的人也能判断它是否符合业务逻辑。相比直接写“测试 discount 函数返回 80”,BDD 更容易暴露需求层面的歧义。
BDD 和 TDD 的区别
| 对比项 | TDD | BDD |
|---|---|---|
| 关注重点 | 代码逻辑是否正确 | 系统行为是否符合业务预期 |
| 主要受众 | 开发者 | 产品、测试、开发 |
| 表达方式 | 单元测试、集成测试 | 业务场景、Given-When-Then |
| 适合阶段 | 编码实现阶段 | 需求澄清和验收标准定义阶段 |
| 核心价值 | 减少代码缺陷,促进可测试设计 | 减少需求误解,让交付结果更贴近业务 |
实用干货:什么时候用 TDD,什么时候用 BDD?
适合优先使用 TDD 的场景
- 核心算法、价格计算、权限判断等逻辑复杂的模块
- 底层工具函数、SDK、公共组件
- 历史代码重构前,需要先建立测试保护网
- 多人协作维护的基础服务
例如订单金额计算、库存扣减、风控规则、AI 应用里的 prompt 参数拼装、模型返回结果解析等,都很适合用 TDD。
适合优先使用 BDD 的场景
- 业务规则容易被误解的需求
- 产品、测试、开发需要频繁对齐的功能
- 涉及用户路径、流程状态、权限角色的系统
- 需要明确验收标准的项目
例如会员升级、优惠券核销、审批流、AI 助手对话流程、多角色后台权限,都很适合先用 BDD 写清楚行为场景。
推荐实践:BDD 管需求,TDD 管实现
很多人会把 BDD 和 TDD 当成二选一,其实更好的方式是把它们组合起来。
- 先用 BDD 对齐业务场景:明确用户在什么条件下做什么操作,系统应该返回什么结果。
- 再用 TDD 拆解实现逻辑:把场景背后的关键函数、服务、边界条件写成测试。
- 最后用自动化测试形成保护网:需求变更或代码重构时,快速发现破坏性修改。
可以把 BDD 看成“验收标准”,把 TDD 看成“实现保障”。BDD 解决“做对事情”,TDD 解决“把事情做对”。
AI 编程时代,BDD 和 TDD 更重要了
现在很多开发者会用 AI 辅助写代码,比如让 AI 生成函数、接口、测试用例、重构方案。但 AI 生成代码有一个明显问题:它可以写得很快,却不一定真的符合你的业务预期。
这时候 BDD 和 TDD 反而更有价值。
- 用 BDD 把业务场景说清楚,减少 AI 对需求的误读。
- 用 TDD 把边界条件固定下来,防止 AI 改出隐藏 bug。
- 让 AI 先根据 Given-When-Then 生成测试,再生成实现代码。
- 每次让 AI 重构前,先确保测试集完整。
一个适合 AI 辅助开发的 Prompt 模板
你是一名资深测试驱动开发工程师。
请根据下面的业务场景,先生成 BDD 风格的验收场景,再拆解出 TDD 单元测试用例,最后再给出实现代码。
业务场景:
用户在购物车中使用优惠券,系统需要计算最终应付金额。
要求:
1. 使用 Given-When-Then 描述业务行为
2. 覆盖正常场景和边界场景
3. 先输出测试用例,再输出实现代码
4. 不要跳过异常情况
这个模板的关键不是让 AI 直接写代码,而是让 AI 先帮你明确行为和测试。这样生成出来的代码更容易被验证,也更容易维护。
落地建议:团队如何开始实践 BDD 和 TDD?
第一步:不要一开始追求全覆盖
很多团队刚开始做 TDD,会因为“测试覆盖率必须很高”而压力过大。更实际的做法是从高风险模块开始,例如支付、订单、权限、计费、AI 输出解析等。
第二步:把需求改写成验收场景
每个需求至少写 2 到 5 个 BDD 场景,包括正常路径、异常路径和边界条件。只要这一步做扎实,后续开发返工会明显减少。
第三步:让测试命名接近业务语言
测试名称不要只写“test case 1”。更推荐写成“当用户使用已过期优惠券时,应返回不可用提示”。这样的测试本身就是文档。
第四步:重构必须依赖测试保护
没有测试保护的重构,本质上是在赌运气。TDD 的价值之一,就是让你敢于优化代码结构,因为测试会告诉你有没有破坏原有行为。
可直接照搬的项目实践清单
- 需求评审时:要求每个关键需求至少提供一个 Given-When-Then 场景。
- 开发前:先把核心逻辑拆成可测试函数或服务,避免所有逻辑堆在控制器、页面组件或回调函数里。
- 写代码时:优先补正常路径测试,再补异常路径和边界值测试。
- 提交代码前:确保新增功能有对应测试,修复 bug 时必须补一个能复现 bug 的测试。
- 使用 AI 时:不要只让 AI 写实现代码,先让它输出验收场景和测试用例。
常见误区
误区一:TDD 会拖慢开发速度
短期看,先写测试确实会多花一些时间。但从完整交付周期看,它能减少调试、返工和线上问题。尤其是复杂业务,TDD 往往是在后半程节省时间。
误区二:BDD 只是换一种格式写测试
BDD 的重点不是格式,而是协作。Given-When-Then 的价值在于让非开发人员也能参与需求澄清。
误区三:有了 AI 就不需要测试
AI 可以提高编码速度,但不能替你承担质量责任。越是依赖 AI 生成代码,越需要用测试来确认结果。
总结
BDD 和 TDD 都不是为了“显得流程专业”,而是为了减少不确定性。
- BDD 帮团队把业务行为讲清楚。
- TDD 帮开发者把代码正确性固定下来。
- AI 编程时代,BDD 和 TDD 可以成为约束 AI 输出质量的重要工具。
最推荐的实践方式是:先用 BDD 写清楚业务场景,再用 TDD 保障核心逻辑实现。这样既能减少需求误解,也能让代码在持续变化中保持稳定。


评论