本节摘要:BDD 把测试从"代码语义"提到"业务语义",用 Given-When-Then 描述行为,让产品和开发、测试、甚至客户说同一种语言,同时天然衔接单元与集成两层。
很多项目里,测试代码是开发的私有语言——断言写得再仔细,产品经理看不懂,新来得同学读起来像天书。当"测试它测了什么"只有一个人知道时,它作为"行为规约"的价值就大打折扣。BDD 想解决的正是这个:让测试的措辞回到业务世界里讲人话,用"如果怎样、当怎样、就怎样"这种场景句式,把行为的"是什么、为什么、值不值"向所有人摊开。
BDD 的核心是三步句式,每个测试不再是一堆断言,而是一句能读的故事:
这套句式的好处是强制你先把"因"和"果"写清楚,很多需求上没说清的边界,在造句阶段就暴露出来。它跟单元测试的"命名纪律"(第 2.4 节)几乎一脉相承——只是从代码命名升格成了一种书写范式。
背景:优惠券系统,需求是"会员用券支付能折上折"。
操作:先和产品用 BDD 句式对齐行为。
给定 我是白金会员 且 我有一张满 100 减 20 的券 当 我结算一笔 150 元的订单 则 我实际应付 100 元 (150 - 20 折上券,充当白金 20%)
结果:开发据此把愿景翻译成单元测试,用"替身会员 + 替身券"在内存里验证。
def test_platinum_with_coupon_on_150(): member = FakeMember("platinum") coupon = FakeCoupon(threshold=100, cut=20) assert settle(150, member, coupon) == 100.0
解读:业务语言和代码一一对应,产品读测试即读需求,矛盾在写码前就被发现。变式:当并到真实计费服务时,再把同一句 Given-When-Then 落成集成用例,用真依赖验证口径——同一段业务故事,单元与集成两层共用。
BDD 的上限是把测试当活文档,但过度追求"全自动化业务故事"会付出三层代价:一是语法层过重,维护句子比维护断言还累;二是容易把所有逻辑都往"行为句"上套,反而抹平了单元与集成的粒度区别;三是团队里没人愿意去跑所谓的高层场景自动化时,句子就变成摆设。
所以现实中的主流做法是轻量 BDD:不强行上整套 BDD 框架,而是在测试命名和组织上借鉴 Given-When-Then 的结构(第 2.4 节的命名范式已经在这么做了),只在真正需要跨角色对齐的高层场景才启动完整的行为自动化。这个分寸,比"上不上 BDD"更值钱。
| 取向 | 做法 | 代价 |
|---|---|---|
| 重 BDD | 全流程行为自动化 | 语法重、维护贵、易失真 |
| 轻 BDD | 命名与结构借 GWT | 轻、活文档价值大 |
| 两者皆可 | 各取所需 | 需弥合两层口径 |
BDD 不是 TDD 的替代品,而是它的"更会说人话的版本"。TDD 关心"何时红灯何时绿"的节奏;BDD 关心"用谁听得懂的话描述要验证的行为"。两者可以叠用:先用 GWT 把需求翻译成一张行为清单,再按 TDD 的红-绿循环一条条把它们变成绿色。先有舌头(BDD 说清要什么),再有力气(TDD 让它绿),产量自然好。
BDD 常被包装成"产品和开发说同一种语言",但这句话真要落地并不容易,它默认了一个前提:双方愿意为了对齐句子而坐下来开会。不少团队跳过这段,只是拉了套 BDD 框架、写了几条 Given-When-Then,结果产品不读、开发自嗨,句子变成仅供展示的摆设。所以别把"共享语言"当成框架白送的礼物——它需要你特意去喂养。
喂养的务实做法有两种。一是把 BDD 句子当成"需求单"而非"测试附件",每次迭代把关键场景句子直接用作任务描述,让句子本身成为沟通契约;二是做"句子评审"——每周花十来分钟,让开发把即将写进测试的 GWT 逐条念给产品听,缺意的当场补。真正让 BDD 值钱的不是语法,而是这个"一个句子大家都能懂"的过程。
刚上手 BDD 的人最常纠结"一个功能要写几条 Given-When-Then"。没有定数,但可以给一把尺:先覆盖业务上"会真实发生"的核心路径,再补"边界与异常",最后才是变体组合。宁可在同一个场景里把断言背后的行为讲透,也别为凑数量发明一堆没人关心的理想场景。一旦场景数目开始失控,多半不是句子不够多,而是最前面那张"行为清单"没画全——回到需求源头把主次理清楚,比埋头补句子更省力。
值得记一句实操心得:高阶的 BDD 场景求少而全,低阶的单元用例求细而深,二者正好对应第 4 章的"集成求真、单元求快"。别把这两个方向的密度搞反了。
如果你所在团队还没碰过 BDD,最不该做的是拉着所有人把全部需求一次性改写成行为句。务实的第一步是挑一条最稳定、跨角色影响最大的主流程先试点:优惠、下单、审批这类"人人都听得懂、又最容易在边界上扯皮"的功能。把这一条用全 GWT 写活,能让产品、开发、测试同时在看到"同一种语言"之后尝到甜头,再谈铺开就顺得多。反过来,一上来就去改写那些连需求方都说不清的内核算法,往往先吵起来的是"这句话到底对不对",而不是 BDD 本身的价值。
给试点定一个"回看窗口"也很关键:跑两三周后,明确回答"句子大家读得懂吗、翻译成测试省不省力、边界提前暴露了几处"。答不上来或全是负反馈,就该回到轻量 BDD,别死磕全套;答得上前三个正面信号,再逐步扩大覆盖范围。BDD 是手段不是竞赛,能不能持续,看的是它有没有真正降低沟通成本。