TDD与BDD:开发方法的本质区别

目录

结论

[1. TDD 是什么?](#1. TDD 是什么?)

[TDD 示例:计算订单折扣](#TDD 示例:计算订单折扣)

第一步:先写测试

第二步:写最少实现代码

第三步:重构

[TDD 关注的是什么?](#TDD 关注的是什么?)

[2. BDD 是什么?](#2. BDD 是什么?)

[BDD 示例:订单折扣](#BDD 示例:订单折扣)

对应的测试代码可能是这样

[3. TDD 和 BDD 的核心区别](#3. TDD 和 BDD 的核心区别)

[4. 用一个登录功能对比](#4. 用一个登录功能对比)

[TDD 写法](#TDD 写法)

[BDD 写法](#BDD 写法)

[5. 工程实践中怎么选?](#5. 工程实践中怎么选?)

[推荐方案:TDD 为主,BDD 只覆盖核心业务链路](#推荐方案:TDD 为主,BDD 只覆盖核心业务链路)

不建议的做法

[6. 简单总结](#6. 简单总结)


前言:TDD主要是关心函数、类的实现是否符合预期,而BDD则是更多地关注业务逻辑是否正确。具体的简化流程就是TDD:先写一个失败的测试案例,然后编写最小改动的实现让测试通过,后续需要有功能的改进需要在保证测试通过的时候进行代码的重构;BDD:给出业务场景会遇到的情况,在每个情况下分别设计1.Scenario ;2.Given ;3.When ;4.Then


结论

TDDBDD 都是"先定义预期,再写实现"的开发方法。

  • TDD:Test-Driven Development,测试驱动开发

    • 关注点:代码逻辑是否正确

    • 主要面向:开发者

    • 常见形式:单元测试、集成测试

    • 核心流程:先写测试 → 测试失败 → 写代码通过测试 → 重构

  • BDD:Behavior-Driven Development,行为驱动开发

    • 关注点:业务行为是否符合预期

    • 主要面向:产品、测试、开发共同理解

    • 常见形式:Given / When / Then 场景描述

    • 核心流程:先描述业务场景 → 再实现功能 → 验证行为


1. TDD 是什么?

TDD 的核心是:

在写业务代码之前,先写测试代码,用测试来反推实现。

典型流程是:

复制代码
Red    :先写一个失败的测试
Green  :写最少代码让测试通过
Refactor:重构代码,保持测试通过

TDD 示例:计算订单折扣

需求:

如果订单金额大于等于 100 元,打 9 折;否则不打折。

第一步:先写测试

python 复制代码
def test_order_discount_when_amount_greater_than_100():
    assert calculate_price(100) == 90
    assert calculate_price(200) == 180


def test_order_no_discount_when_amount_less_than_100():
    assert calculate_price(99) == 99

这时候 calculate_price 还不存在,所以测试一定失败。

这就是 Red 阶段


第二步:写最少实现代码

python 复制代码
def calculate_price(amount):
    if amount >= 100:
        return amount * 0.9
    return amount

现在测试通过。

这就是 Green 阶段


第三步:重构

比如考虑金额精度:

python 复制代码
from decimal import Decimal


def calculate_price(amount):
    amount = Decimal(str(amount))

    if amount >= Decimal("100"):
        return amount * Decimal("0.9")

    return amount

只要测试仍然通过,就说明重构没有破坏原有逻辑。


TDD 关注的是什么?

TDD 主要关注:

复制代码
这个函数、类、模块的逻辑是否正确?
边界条件是否覆盖?
重构时是否会破坏已有行为?

它更偏向 代码层面的正确性保障


2. BDD 是什么?

BDD 的核心是:

用接近自然语言的方式描述业务行为,然后用这些行为场景来驱动开发和测试。

BDD 常用格式是:

复制代码
Given 前置条件
When  发生某个动作
Then  应该得到某个结果

BDD 示例:订单折扣

同样的需求:

如果订单金额大于等于 100 元,打 9 折;否则不打折。

用 BDD 可以这样写:

复制代码
Feature: 订单折扣

  Scenario: 订单金额达到 100 元时享受 9 折优惠
    Given 用户有一个金额为 100 元的订单
    When 系统计算订单最终价格
    Then 最终价格应该是 90 元

  Scenario: 订单金额低于 100 元时不享受优惠
    Given 用户有一个金额为 99 元的订单
    When 系统计算订单最终价格
    Then 最终价格应该是 99 元

这段不是普通测试代码,而是业务场景说明。

它的价值在于:

复制代码
产品能看懂
测试能看懂
开发能实现
业务方也能参与讨论

对应的测试代码可能是这样

以 Python behave 风格举例:

python 复制代码
@given("用户有一个金额为 {amount:d} 元的订单")
def step_given_order(context, amount):
    context.amount = amount


@when("系统计算订单最终价格")
def step_when_calculate_price(context):
    context.final_price = calculate_price(context.amount)


@then("最终价格应该是 {expected:d} 元")
def step_then_check_price(context, expected):
    assert context.final_price == expected

BDD 的场景描述和测试代码是绑定的。


3. TDD 和 BDD 的核心区别

对比项 TDD BDD
全称 Test-Driven Development Behavior-Driven Development
中文 测试驱动开发 行为驱动开发
关注点 代码逻辑正确性 业务行为符合预期
主要使用者 开发者 产品、测试、开发
表达方式 测试代码 Given / When / Then
常见粒度 函数、类、模块 用户场景、业务流程
典型工具 pytest、JUnit、Go test Cucumber、behave、SpecFlow
更适合 算法、服务逻辑、核心模块 复杂业务流程、验收测试

4. 用一个登录功能对比

TDD 写法

关注的是函数是否返回正确结果。

python 复制代码
def test_login_success():
    user = User(username="alice", password="123456")

    result = login("alice", "123456")

    assert result.success is True
    assert result.user.username == "alice"


def test_login_failed_with_wrong_password():
    result = login("alice", "wrong-password")

    assert result.success is False
    assert result.error == "用户名或密码错误"

这里主要验证的是:

复制代码
login 函数在不同输入下是否返回正确结果

BDD 写法

关注的是用户行为和系统响应。

复制代码
Feature: 用户登录

  Scenario: 用户使用正确密码登录成功
    Given 系统中存在用户名为 "alice" 且密码为 "123456" 的用户
    When 用户输入用户名 "alice" 和密码 "123456" 进行登录
    Then 系统应该允许用户登录
    And 用户应该进入首页

  Scenario: 用户使用错误密码登录失败
    Given 系统中存在用户名为 "alice" 且密码为 "123456" 的用户
    When 用户输入用户名 "alice" 和密码 "wrong-password" 进行登录
    Then 系统应该拒绝登录
    And 页面应该提示 "用户名或密码错误"

这里更强调:

复制代码
从用户视角看,系统行为是否符合业务预期

5. 工程实践中怎么选?

推荐方案:TDD 为主,BDD 只覆盖核心业务链路

大多数研发团队更适合这样用:

复制代码
核心函数、服务逻辑、边界条件:用 TDD
核心业务流程、验收场景:用 BDD

例如:

场景 推荐方式
价格计算 TDD
权限判断 TDD
数据清洗 TDD
API 参数校验 TDD
用户下单流程 BDD
支付成功后的订单状态流转 BDD
医疗问诊流程 BDD
Agent 工具调用流程验收 BDD

不建议的做法

不要为了"流程规范"把所有东西都写成 BDD。

比如一个简单函数:

复制代码
def add(a, b):
    return a + b

写成 BDD 就很重:

复制代码
Given 用户输入数字 1 和 2
When 系统执行加法
Then 系统应该返回 3

这类场景用普通单元测试就够了。


6. 简单总结

可以这样理解:

复制代码
TDD:从代码正确性出发
BDD:从业务行为出发

更直观一点:

复制代码
TDD 问的是:这个函数对不对?
BDD 问的是:这个系统行为是不是用户想要的?

工程上比较成熟的做法是:

复制代码
多数逻辑用 TDD 保证质量
少数关键业务链路用 BDD 做验收
不要把 BDD 滥用成高成本文档工程
相关推荐
互联网工匠2 天前
TDD在古法编程时代的困境及AI编程时代的转机
人工智能·ai编程·tdd
Rubin智造社2 天前
智读致用|《谷歌亚马逊如何做产品》5|赢在测试:从羞耻心到高质量交付的实战体系
测试驱动开发·tdd·bug管理·亲友蒙羞测试·谷歌亚马逊工作法·内部试用·找虫总动员
一路往蓝-Anbo5 天前
第四章:手撕协议栈 —— 缓冲区与结构体数据的 Mock 技巧
网络·stm32·单片机·嵌入式硬件·软件工程·tdd
一路往蓝-Anbo5 天前
第二章:隔离硬件 —— 利用 CMock 伪造 GPIO 与定时器
stm32·单片机·嵌入式硬件·软件工程·信息与通信·tdd
一路往蓝-Anbo6 天前
第一章:嵌入式TDD-环境搭建
网络·stm32·单片机·嵌入式硬件·tdd
低调小一12 天前
Midscene.js 原理拆解:它不是“自然语言点按钮”,而是一套会看屏幕的 UI 自动化运行时
人工智能·rnn·架构·大模型·transformer·tdd·midscene
阿维的博客日记13 天前
什么是TDD for AI,详细讲一下
人工智能·tdd
优化Henry19 天前
TDD-LTE站点Rilink=3链路故障处理案例---BBU侧C口“有发光、无收光”的排查与恢复
运维·网络·信息与通信·tdd
低调小一23 天前
BDD(行为驱动开发)入门:把“测试”写成“行为”,把“需求”写成“场景”
驱动开发·tdd·bdd