ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

用Python进行行为驱动开发的入门教程

用Python进行行为驱动开发的入门教程

前言


行为驱动开发(Behavior-Driven Development,BDD)常被误解成「一种测试框架」或「写测试的另一种花样」。实际上它首先是一种沟通方式:把需求用接近自然语言的句式写出来,让业务方、产品、开发、测试能一起读懂同一份描述,再让这份描述直接驱动自动化测试。


在 Python 里,BDD 靠第三方库落地,常见的有behave和pytest-bdd,它们都不属于标准库,需要单独安装。本文讲的是思路与骨架,具体的装饰器名、参数与匹配规则以所用框架的官方文档为准——不同版本的细节可能不同,不要照抄记忆里的写法。


文章会覆盖三块:BDD 与 TDD 的定位差别、Gherkin 语法(Given / When / Then)、以及一个behave风格的目录骨架。最后说清它到底适合什么样的团队,不适合的也别硬上。


一、BDD 与 TDD 的定位差异


先把两个词摆正位置。


TDD(Test-Driven Development,测试驱动开发)是开发者内部的工作循环:先写一个会失败的单元测试,再写刚好让它通过的代码,最后重构。它关注的是代码单元的正确性,语言是程序员的语言(函数、类、断言)。


BDD往外扩了一层:它关注的是系统行为是否满足业务期望,语言是业务的语言(用户、场景、期望结果)。BDD 的产物——场景描述——应当能被非技术人员看懂并参与评审。




维度TDDBDD



关注点代码单元正确系统行为符合业务

语言代码 / 断言自然语言(Gherkin)

参与者开发者业务 + 开发 + 测试

粒度函数 / 类用户场景 / 端到端行为

典型工具unittest、pytestbehave、pytest-bdd

是否标准库是否(第三方)



它们不是替代关系。BDD 场景通常还需要底层的单元测试兜底——BDD 负责「做对了事」,TDD/单元测试负责「事做对了」。


二、Gherkin 语法:Given / When / Then


Gherkin 是 BDD 用来写场景的结构化自然语言。它不是英文独有,只是关键词固定。核心结构是一个Feature(功能)下面挂若干Scenario(场景),每个场景由三类步骤组成:



  • Given:前置条件,把系统摆到起点状态(「假设用户已登录」);

  • When:触发动作,描述用户做了什么(「当他提交订单」);

  • Then:期望结果,验证可观察的结果(「那么订单状态应为已支付」)。


配套还有And/But作为Given、When、Then的延续(语义上跟随上一步的类型),以及Background(所有场景共用的前置)、Scenario Outline+Examples(参数化场景)。


Feature: 购物车结算

Background:
Given 用户已登录并且购物车为空

Scenario: 把商品加入购物车
When 用户把「机械键盘」加入购物车
Then 购物车里应有 1 件商品
And 商品名应为「机械键盘」

Scenario Outline: 多件商品累计金额
When 用户把「<name>」以 <price> 元加入购物车
Then 购物车总金额应为 <total> 元

Examples:
| name | price | total |
| 鼠标垫 | 30 | 30 |
| 机械键盘 | 200 | 230 |

Scenario Outline用一张参数表把「同一条规则、不同数据」的场景压成一条,避免复制粘贴。这条写法在 Gherkin 里是通用的,无论用哪个框架。


三、目录结构与步骤实现


以behave的常见组织方式为例(具体以该框架官方文档为准):


project/
├── features/
│ ├── cart.feature # Gherkin 场景
│ ├── environment.py # 前后置钩子(可选)
│ └── steps/
│ └── cart_steps.py # 步骤的实现代码
└── src/
└── cart.py

每个Feature文件放在features/下;steps/目录里放 Python 代码,把 Gherkin 里的每一句话绑定到一个函数上。绑定靠装饰器配一个匹配模式,函数从步骤文本里取参数,并通过一个共享的context对象在步骤之间传递状态。


# 适用于 Python 3.8+;需要第三方库 behave,装饰器与 API 以官方文档为准
from behave import given, when, then
from src.cart import Cart

@given("用户已登录并且购物车为空")
def step_impl(context):
context.cart = Cart()

@when('用户把"{name}"加入购物车')
def step_impl(context, name):
context.cart.add(name)

@then("购物车里应有 {count:d} 件商品")
def step_impl(context, count):
assert len(context.cart.items) == count

@then('商品名应为"{name}"')
def step_impl(context, name):
assert context.cart.items[-1] == name

这里有几个要点:



  • 步骤文本与装饰器里的模式必须匹配。花括号{name}是占位符,默认按字符串解析;{count:d}带类型标记,会转成整数。具体占位符语法以框架文档为准。

  • context是步骤之间唯一的共享通道:上一步存进去,下一步取出来。不要在步骤函数里用全局变量传状态。

  • 同一个步骤文本在多个 feature 里可以复用同一份实现,这正是 BDD 省事的地方。

  • 步骤实现应当薄,真正的业务逻辑留在src/里,步骤只做「翻译 + 断言」。


如果用pytest-bdd,思路一样,只是它挂在 pytest 生态里,用 pytest 的 fixture 代替context,并且能复用 pytest 的插件与报告。选哪个取决于团队是否已经在用 pytest。


四、BDD 适合什么样的团队


BDD 不是万能药,它有明确的适用边界。


适合的情况:



  • 业务方愿意参与需求评审,能一起看场景、提修改意见;

  • 需求以「用户行为」为主,比如电商下单、审批流程、账户操作——这些用 Given/When/Then 描述非常自然;

  • 团队需要一个跨角色的共同词汇表,减少「开发理解的需求」和「业务想要的需求」之间的偏差。


不适合硬上的情况:



  • 纯算法 / 底层库开发,没什么「用户行为」可描述,写成场景反而绕;

  • 只有开发者一个人、没人参与评审——那额外维护一层 Gherkin 只是负担;

  • 团队还不具备自动化测试基础设施,先补单元测试的坑更实在。


一句话:BDD 的成本在「维护自然语言与实现的一致性」,收益在「跨角色沟通」。如果沟通本身就是痛点,它划算;如果只是想要个测试跑起来,直接用unittest或 pytest 就好。


常见坑点



  1. 把 BDD 当成「更高级的测试框架」


❌ 只关心场景能不能跑,不管业务方是否真在看


✅ 把它当沟通工具,场景要能过业务评审



  1. 步骤文本与实现模式对不上


❌ Gherkin 里写「用户登录」,代码里@given("用户已登录")


✅ 两边逐字对齐,或统一由步骤模板生成



  1. 把业务逻辑写进步骤函数


❌ 步骤里堆几百行计算


✅ 步骤只做翻译与断言,逻辑留在被测代码里



  1. 用全局变量在步骤间传状态


❌ 步骤之间靠模块级变量通信


✅ 用框架提供的context/ fixture



  1. 场景写得过于技术化


❌ 「Then 数据库表 orders 的 status 字段等于 1」


✅ 「Then 订单状态应为已支付」



  1. 每个测试点都写一条 Scenario


❌ 复制十几条只差数据的场景


✅ 用Scenario Outline+Examples参数化



  1. 拿 BDD 场景替代单元测试


❌ 所有验证都堆在端到端场景里,慢且难定位


✅ BDD 覆盖主流程,细节逻辑仍由单元测试兜底



  1. 假设框架 API 通用


❌ 把 behave 的写法直接搬到 pytest-bdd 上


✅ 以所用框架官方文档为准,不混用


总结




要素含义落地方式



Feature一个功能一个.feature文件

Scenario一个用户场景Given / When / Then

Scenario Outline参数化场景配Examples表格

Step场景里的每一步绑定到一个 Python 函数

context步骤间共享状态框架提供的对象

角色业务 + 开发 + 测试共同评审场景



BDD 的价值不在语法本身,而在于让「需求」和「验证」用同一份文本表达。如果团队里真的有人会读这份文本,它就能省下大量的沟通成本;如果没有人读,那它只是一层多余的外壳。




RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进