- Published on
TDD、ATDD、BDD:三者到底差在哪?

測試系列文章
- Test Double 測試替身入門:五種類型一次搞懂
- TDD 的兩大學派:底特律 (Detroit) vs 倫敦 (London),你是哪一派?
- 單元測試和 TDD 之間的關係
- TDD、ATDD、BDD:三者到底差在哪? (本篇)
- AI 時代,TDD 不是過時了,而是更重要了
- SDD — 當規格成為 AI 的導航系統
你一定有聽過這些對話
「我們要用 TDD 還是 BDD?」 「ATDD 跟 BDD 到底差在哪?」 「BDD 不是要寫 Given/When/Then 嗎?那跟 TDD 完全不一樣吧?」
每次講到這個話題,有可能就會吵成一團。有人堅持 BDD 比 TDD 更先進,有人覺得 ATDD 才是正解,看了幾篇文章後更混亂。這三個東西怎麼看起來差不多,又好像不一樣?
這篇文章要告訴你一個可能讓你意外的結論:在寫程式碼的層面,TDD、ATDD、BDD 本質上沒有什麼不同。差異主要在框架和語法包裝。而 BDD 真正有價值的地方,不在寫程式碼,而在 Specification by Example (SBE),也就是在寫任何程式碼之前,跟使用者和 PM 用具體的例子討論出行為規格
時空背景:從 TDD 到 BDD 的演進
在展開細節之前,先用一個時間表理清這三個方法論的來龍去脈
| 年份 | 事件 | 關鍵人物 | 重要著作/工具 |
|---|---|---|---|
| 1996 | Kent Beck 在 C3 專案中實踐 test-first programming | Kent Beck | - |
| 1997 | JUnit 誕生 (飛機上的 pair programming) | Kent Beck, Erich Gamma | JUnit |
| 2002 | TDD 經典書籍出版 | Kent Beck | ”Test Driven Development: By Example” |
| 2003–2004 | Dan North 構思 BDD 並釋出 JBehave | Dan North | JBehave (初代) |
| 2006 | Dan North 正式發表 BDD 文章 | Dan North | ”Introducing BDD” |
| 2008 | Cucumber 誕生,Gherkin 語法流行 | Aslak Hellesøy | Cucumber |
| 2009 | GOOS 出版,Outside-in TDD / Double-loop TDD 經典著作 | Steve Freeman, Nat Pryce | ”Growing Object-Oriented Software, Guided by Tests” |
| 2011 | SBE (實例化需求) 概念被系統化 | Gojko Adzic | ”Specification by Example” |
看完這張表,你會發現一件事:BDD 和 ATDD 幾乎在同一個時期誕生,而且它們要解決的問題有很大的重疊。接下來我們逐一拆開來看
TDD:開發者的設計工具 (1996~)
TDD 的故事在前面的系列文章中已經講得很詳細了,這裡簡短回顧就好
Kent Beck 在 1996 年參與 Chrysler 的 C3 薪資系統專案時,帶進了「先寫測試再寫程式碼」的開發方式。這個方式後來在 2002 年被系統化地寫進了 “Test Driven Development: By Example” 這本書裡
核心循環很簡單
RED → 寫一個失敗的測試 ↓ GREEN → 寫最少的程式碼讓測試通過 ↓ REFACTOR → 重構,保持測試通過 ↓ 回到 RED
補充一點:在強型別語言 (如 Java、C#、TypeScript) 中,編譯失敗也算是 RED 的一部分。你先寫了一個呼叫還不存在的方法的測試,編譯器報錯 — 這就是 RED。接著你建立那個方法的介面讓編譯通過,再讓測試通過 — 這就是 GREEN。TDD 不只驅動邏輯實作,也驅動介面設計
TDD 的重點不在「測試」,而在「用測試來驅動設計」。測試只是副產品,真正值錢的是小步前進的開發節奏和逐步浮現的設計
TDD 框架的寫法
在 JavaScript 生態系中,TDD 風格的測試框架長什麼樣子?來看三個主流框架
// Vitest
import { describe, it, expect } from 'vitest'
import { add } from './calculator.js'
describe('add', () => {
it('should return sum of two numbers', () => {
expect(add(2, 3)).toBe(5)
})
})
// Jest (幾乎一模一樣)
import { add } from './calculator.js'
describe('add', () => {
it('should return sum of two numbers', () => {
expect(add(2, 3)).toBe(5)
})
})
// Mocha + Chai
import { expect } from 'chai'
import { add } from './calculator.js'
describe('add', () => {
it('should return sum of two numbers', () => {
expect(add(2, 3)).to.equal(5)
})
})
三個框架的語法幾乎一樣,只有斷言 (assertion) 的部分稍有差異。這裡先記住一件事:describe 和 it 這兩個函式,等等講到 BDD 的時候你會發現,這個語法本身就已經是 BDD 風格了
BDD:從「測試」到「行為」的思維轉換 (2003~)
起源:一個困擾新手的詞
2003 年,Dan North 在倫敦教人寫 TDD。他發現一個有趣的現象:新手對 “test” 這個詞感到困惑
新手常常會問這些問題
- 我應該從哪裡開始寫測試?
- 我應該測試什麼、不測試什麼?
- 一個測試要測多少東西?
- 測試應該怎麼命名?
Dan North 意識到,這些問題的根源在於 “test” 這個詞帶給人的心智模型。大家聽到「測試」就會想到「驗證」、「QA」、「事後檢查」。但 TDD 的測試根本不是這個意思。TDD 的測試是在描述行為、定義規格
所以 Dan North 做了一件事:把所有跟 “test” 相關的詞彙,全部換成跟 “behaviour” 相關的詞彙
他在 JBehave 這個框架中做了這件事,把 TDD 的術語全部重新命名
語言轉變:從測試到行為
| TDD 術語 | BDD 術語 | 說明 |
|---|---|---|
| test | spec (specification) | 測試 → 規格 |
| test case | scenario | 測試案例 → 情境 |
| test suite | feature / story | 測試套件 → 功能 / 使用者故事 |
| assertion | expectation | 斷言 → 期望 |
| test method | should… / it… | 測試方法 → 應該… |
| setUp | given (context) | 前置設定 → 給定 (情境) |
| exercise | when (event) | 執行 → 當 (事件發生) |
| verify | then (outcome) | 驗證 → 那麼 (結果) |
Dan North 在 “Introducing BDD” 這篇文章中寫道
「我發現當我把 ‘test’ 換成 ‘should’ 的時候,所有關於測試的問題都消失了。人們不再問『我應該測什麼?』,而是開始問『這個東西應該做什麼?』」
這個思維轉換看起來很小,但想問題的角度會完全不同。當你把「測試加法函式」改成「描述加法函式應該有的行為」,整個思考的出發點就不一樣了
BDD 框架 — 程式碼層級
先看程式碼層級的 BDD。如果你有在寫 JavaScript 測試,你其實每天都在用 BDD 風格的語法
// 這就是 BDD 風格 — describe/it 語法
import { describe, it, expect } from 'vitest'
import { add } from './calculator.js'
describe('add', () => {
it('should return 5 when adding 2 and 3', () => {
expect(add(2, 3)).toBe(5)
})
it('should return 0 when adding 0 and 0', () => {
expect(add(0, 0)).toBe(0)
})
it('should handle negative numbers', () => {
expect(add(-1, 1)).toBe(0)
})
})
等一下,這不就是剛才 TDD 的範例嗎?
沒錯。describe / it 這組語法,本身就是 BDD 風格。describe 描述的是一個功能 (feature),it 描述的是一個行為 (behaviour),it('should ...') 就是在說「它應該…」
所以在程式碼層級,TDD 和 BDD 根本就是同一件事,差別只在你怎麼命名和怎麼思考
有些框架會提供更 BDD 風格的語法糖,像是 Mocha + Chai 搭配 expect 的鏈式語法
// Mocha + Chai 的 BDD 風格
import { expect } from 'chai'
import { add } from './calculator.js'
describe('Calculator', () => {
context('when adding two positive numbers', () => {
it('should return their sum', () => {
expect(add(2, 3)).to.equal(5)
})
})
context('when adding negative numbers', () => {
it('should handle them correctly', () => {
expect(add(-1, -2)).to.equal(-3)
})
})
})
多了一個 context 可以用,讀起來更像自然語言。但底層做的事情跟 TDD 完全一樣
BDD 框架 — 驗收層級 (Cucumber + Gherkin)
BDD 真正跟 TDD 拉開差距的地方,是在驗收層級。這裡要介紹 Cucumber 和 Gherkin 語法
Gherkin 是一種用自然語言撰寫的測試格式,長這個樣子
# calculator.feature
Feature: 計算機
作為使用者
我想要進行加法運算
以便計算數字的總和
Scenario: 兩個正數相加
Given 我輸入了 2
And 我輸入了 3
When 我按下加法
Then 結果應該是 5
Scenario: 負數相加
Given 我輸入了 -1
And 我輸入了 -2
When 我按下加法
Then 結果應該是 -3
Gherkin 的重點是:非技術人員也看得懂。PM、BA、甚至使用者都可以讀這段文字,確認需求是不是被正確理解了
但 .feature 檔案本身不能執行,需要搭配 Step Definitions 把每個步驟對應到程式碼
// calculator.steps.js
import { Given, When, Then } from '@cucumber/cucumber'
import { expect } from 'chai'
// 使用 World context (this) 管理狀態,讓每個 Scenario 互不干擾
// 注意:這裡必須使用 function() {},不能用 arrow function,否則拿不到 this
Given('我輸入了 {int}', function (number) {
if (!this.numbers) this.numbers = []
this.numbers.push(number)
})
When('我按下加法', function () {
const nums = this.numbers || []
this.result = nums.reduce((sum, n) => sum + n, 0)
this.numbers = []
})
Then('結果應該是 {int}', function (expected) {
expect(this.result).to.equal(expected)
})
看到了嗎?Step Definitions 裡面做的事情就是:設定狀態、執行動作、檢查結果。本質上跟 Arrange-Act-Assert 或者 Given-When-Then 的 pattern 完全一樣
Cucumber + Gherkin 的價值不在於它讓測試變得更厲害,而在於它提供了一種讓技術和非技術人員能用同一份文件溝通的方式。這一點後面講 SBE 的時候會再展開
ATDD:雙迴圈的 TDD (2003~2009)
起源:GOOS 書的雙迴圈
ATDD (Acceptance Test-Driven Development,驗收測試驅動開發) 的概念在 2009 年被 Steve Freeman 和 Nat Pryce 在 “Growing Object-Oriented Software, Guided by Tests” (GOOS) 這本書中完整地呈現
GOOS 提出了一個核心概念:雙迴圈 TDD (Double Loop TDD)
外部迴圈是一個驗收測試 (Acceptance Test)。你先寫一個端到端的測試,描述一個使用者可見的功能。這個測試一開始當然會失敗
然後你進入內部迴圈,用標準的 TDD (Red-Green-Refactor) 一步步實作功能。每次完成一個內部迴圈,就往外部迴圈的目標前進一點。當所有需要的內部迴圈都完成後,外部迴圈的驗收測試也跟著通過了
用白話文來說就是:大的失敗測試驅動方向,小的失敗測試驅動實作
外部迴圈回答「我們要做什麼」,內部迴圈回答「怎麼做」。外部迴圈確保你沒有偏離需求,內部迴圈確保你的程式碼品質
ATDD 範例
用一個實際的例子來看。假設需求是「使用者可以新增待辦事項」
首先,寫一個驗收測試 (外部迴圈)
// 驗收測試 — 用 Playwright 撰寫
import { test, expect } from '@playwright/test'
test('user can add a todo item', async ({ page }) => {
// Given - 使用者在待辦事項頁面
await page.goto('/todos')
// When - 輸入待辦事項並送出
await page.fill('[data-testid="todo-input"]', '買牛奶')
await page.click('[data-testid="add-button"]')
// Then - 待辦事項出現在列表中
const todoList = page.locator('[data-testid="todo-list"]')
await expect(todoList).toContainText('買牛奶')
})
這個驗收測試會失敗,因為什麼都還沒有實作。接著進入內部迴圈,用 TDD 一步步實作
// 內部迴圈 — 第一個單元測試
import { describe, it, expect } from 'vitest'
import { TodoList } from './todo-list.js'
describe('TodoList', () => {
it('should add a new todo item', () => {
const todoList = new TodoList()
todoList.add('買牛奶')
expect(todoList.items).toEqual(['買牛奶'])
})
it('should start with an empty list', () => {
const todoList = new TodoList()
expect(todoList.items).toEqual([])
})
})
// todo-list.js
export class TodoList {
constructor() {
this.items = []
}
add(item) {
this.items.push(item)
}
}
單元測試通過了。繼續實作 UI 元件、路由、頁面…直到最外層的驗收測試也通過
你可能已經發現了:ATDD 的驗收測試也可以用 Cucumber 的 Gherkin 語法來寫
Feature: 待辦事項
Scenario: 新增一筆待辦事項
Given 我在待辦事項頁面
When 我輸入「買牛奶」
And 我按下新增按鈕
Then 我應該在列表中看到「買牛奶」
所以 ATDD 和 BDD 在驗收層級幾乎可以互換。不過如果要咬文嚼字,兩者的出發點還是有微妙的不同:ATDD 的重心在「自動化驗收測試」,也就是用可執行的測試來確認功能是否符合驗收條件;BDD 的重心在「用領域語言促進溝通」,也就是讓不同角色能用同一套語言描述系統行為。實務上兩者通常走到同一個終點 — 一份可執行的規格文件,只是走的路徑不一樣
接下來看看框架層面的全貌
框架比較總覽
講了這麼多框架,來做一個統整。用同一個邏輯 (add(2, 3) 應該等於 5),看看三種風格的寫法
// xUnit 風格 (傳統 TDD:describe + test + assert)
describe('add', () => {
test('returns sum of two numbers', () => {
assert.equal(add(2, 3), 5)
})
})
// BDD 風格 (describe + it + expect)
describe('add', () => {
it('should return the sum of two numbers', () => {
expect(add(2, 3)).toBe(5)
})
})
// Gherkin 風格 (驗收層級 BDD)
// Given 我有數字 2 和 3
// When 我將它們相加
// Then 結果應該是 5
有注意到嗎?xUnit 風格跟 BDD 風格的差異,真的只在命名和語法糖。底層做的事情完全一樣:準備資料、執行動作、驗證結果
真相:程式碼層面沒有本質差異
這是本文最核心的論點
好,到了這篇文章最重要的部分。我們直接用程式碼來說明
// === TDD 風格 (xUnit 慣例:describe + test + assert) ===
import { describe, test, assert } from 'vitest'
import { add } from './calculator.js'
describe('Calculator', () => {
describe('add', () => {
test('returns 5 for inputs 2 and 3', () => {
// Arrange
const a = 2
const b = 3
// Act
const result = add(a, b)
// Assert
assert.equal(result, 5)
})
})
})
// === BDD 風格 (describe + it + expect) ===
import { describe, it, expect } from 'vitest'
import { add } from './calculator.js'
describe('Calculator', () => {
describe('add', () => {
it('should return the sum when given two positive numbers', () => {
// Given
const a = 2
const b = 3
// When
const result = add(a, b)
// Then
expect(result).toBe(5)
})
})
})
把這兩段程式碼放在一起看。到底差在哪裡?
test換成了it(兩者都包在describe裡面,結構一樣)assert.equal換成了expect().toBe()- 註解從
Arrange/Act/Assert換成了Given/When/Then - 測試命名從陳述句 (
returns 5) 換成了行為描述句 (should return the sum)
就這樣。執行的邏輯完全一樣,驗證的結果完全一樣,跑測試的方式也完全一樣
甚至在 Vitest 和 Jest 中,test 和 it 就是同一個函式的 alias。你可以在同一個測試檔案中混用,測試框架完全不在乎
// Vitest 中,test 和 it 是同一個東西
import { describe, it, test, expect } from 'vitest'
test('這是 TDD 風格', () => {
expect(1 + 1).toBe(2)
})
it('should be BDD style', () => {
expect(1 + 1).toBe(2)
})
// 以上兩個測試對框架來說完全沒有差異
不過實務上,團隊通常會統一用其中一種。像 eslint-plugin-vitest (或 Jest 生態系的 eslint-plugin-jest) 就有一條 consistent-test-it 規則,可以強制整個專案只能用 test 或只能用 it。所以你有時候會看到團隊為了這個風格爭論,其實背後是 linter 在幫你管
所以當有人在會議上說「我們應該從 TDD 轉到 BDD」,而他只是想把 test 換成 it,那你可以告訴他:你已經在用 BDD 風格的語法了,你要談的應該不是框架,而是團隊的溝通方式
BDD 真正的價值:Specification by Example (SBE)
既然程式碼層面沒有本質差異,那 BDD 存在的意義是什麼?
答案是 Specification by Example (SBE),用具體的例子來定義需求規格
什麼是 SBE
傳統的需求文件長這樣
「系統應根據會員等級提供相應的折扣優惠。不同等級的會員享有不同的折扣比例,折扣適用於結帳時的商品總金額」
看完之後你知道要做什麼嗎?大概知道方向,但細節呢?什麼等級打幾折?有沒有最低消費門檻?折扣可以跟其他優惠疊加嗎?
SBE 的做法是:不寫抽象的規格說明,而是用具體的例子來定義需求
Feature: 會員等級折扣
Scenario: 金卡會員購買超過 1000 元享 8 折
Given 一位「金卡」等級的會員
And 購物車中有一件 1500 元的商品
When 結帳
Then 應付金額為 1200 元
Scenario: 一般會員不享有折扣
Given 一位「一般」等級的會員
And 購物車中有一件 1500 元的商品
When 結帳
Then 應付金額為 1500 元
Scenario: 金卡會員購買未達 1000 元不打折
Given 一位「金卡」等級的會員
And 購物車中有一件 800 元的商品
When 結帳
Then 應付金額為 800 元
差別就在這裡。每個 Scenario 都是一個具體的例子,有明確的輸入和期望輸出。任何人(PM、開發者、測試人員)看了都知道這個功能應該怎麼運作
Three Amigos 會議
SBE 最核心的實踐,通常透過 Three Amigos 會議 (三劍客會議,Gojko Adzic 在書中稱為 Specification Workshop) 來進行。在寫任何程式碼之前,三種角色要坐在一起討論
- PM / BA / PO:定義業務需求和驗收條件
- 開發者:評估技術可行性,提出邊界案例
- 測試人員:挑戰假設,找出沒有被考慮到的情境
不是每個團隊都有獨立的測試人員。在很多敏捷團隊中,QA 的角色可能由開發者兼任,或者根本沒有專職 QA。這種時候 Three Amigos 就變成「Two Amigos」— PM/PO 加上開發者。重點不是一定要湊滿三個角色,而是確保「定義需求的人」和「實作需求的人」有坐下來用具體的例子對齊認知
他們一起用具體的例子把需求釘死。這個過程才是 BDD 真正有價值的地方
拿「會員等級折扣」來說,Three Amigos 會議中可能會出現這些對話
- PM:「金卡會員購物打 8 折」
- 開發者:「所有商品都打 8 折嗎?有沒有例外?」
- PM:「嗯…特價商品不打折」
- 測試人員:「如果購物車裡同時有特價和非特價商品呢?」
- PM:「只有非特價商品的部分打 8 折」
- 開發者:「有最低消費門檻嗎?買一杯 50 元的飲料也打 8 折?」
- PM:「好問題,消費滿 1000 元以上才打折」
- 測試人員:「1000 元是折扣前還是折扣後?」
- PM:「折扣前」
看到了嗎?一場 30 分鐘的 Three Amigos 會議,就釐清了四五個原本可能要等到開發完成後才會被發現的邊界案例。每個釐清的問題,都會變成一個新的 Scenario
Feature: 會員等級折扣
Scenario: 金卡會員購買超過 1000 元享 8 折
Given 一位「金卡」等級的會員
And 購物車中有一件 1500 元的非特價商品
When 結帳
Then 應付金額為 1200 元
Scenario: 金卡會員購買未達 1000 元不打折
Given 一位「金卡」等級的會員
And 購物車中有一件 800 元的非特價商品
When 結帳
Then 應付金額為 800 元
Scenario: 特價商品不適用會員折扣
Given 一位「金卡」等級的會員
And 購物車中有一件 1500 元的特價商品
When 結帳
Then 應付金額為 1500 元
Scenario: 混合購物車只有非特價商品打折
Given 一位「金卡」等級的會員
And 購物車中有一件 1200 元的非特價商品
And 購物車中有一件 500 元的特價商品
When 結帳
Then 非特價商品金額為 1200 元,適用 8 折為 960 元
And 特價商品金額為 500 元,不適用折扣
And 應付總金額為 1460 元
Scenario: 一般會員不享有折扣
Given 一位「一般」等級的會員
And 購物車中有一件 1500 元的非特價商品
When 結帳
Then 應付金額為 1500 元
這些 Scenario 就是 Living Documentation (活文件)。它們既是需求規格、也是驗收測試,還能當溝通工具用。而且因為它們可以直接透過 Cucumber 執行,所以不會像傳統的 Word 文件一樣過期
真正的 BDD vs 假的 BDD
理解了 SBE 之後,就可以分辨什麼是「真正的 BDD」和「假的 BDD」
假的 BDD:只是換了語法的 TDD
開發者自己一個人坐在電腦前,把原本的 xUnit 風格測試用 Given/When/Then 包裝起來
// 假的 BDD — 開發者自己寫的,沒有跟任何人討論
describe('MemberDiscount', () => {
it('should apply 20% discount for gold member', () => {
// Given
const member = new Member('gold')
const cart = new Cart()
cart.addItem({ price: 1500 })
// When
const total = checkout(member, cart)
// Then
expect(total).toBe(1200)
})
})
這段測試本身沒什麼問題,但它不是 BDD。它只是一個用 describe/it 語法寫的單元測試。開發者根據自己的理解定義了「金卡打 8 折」的規則,但這個理解可能是錯的。也許 PM 想的是「金卡打 85 折」,也許測試人員會發現「特價商品不打折」這個規則被遺漏了
真正的 BDD:先討論出規格,再寫程式碼
真正的 BDD 流程是
- Three Amigos 會議:PM、開發者、測試人員一起討論,用具體的例子定義行為規格
- 撰寫 Feature 檔案:把討論出來的例子用 Gherkin 語法寫成
.feature檔案 - 實作 Step Definitions:開發者把 Gherkin 步驟對應到程式碼
- TDD 內部迴圈:用標準的 TDD 流程實作功能細節
你看,真正的 BDD 包含了 TDD,但在 TDD 之前多了一個步驟:跟利害關係人 (stakeholders) 一起用具體的例子釐清需求。這裡說的利害關係人,就是前面 Three Amigos 會議中提到的那些角色 — PM/PO、開發者、測試人員,或者任何會被這個功能影響到的人
Dan North 自己也說過
「BDD is a second-generation, outside-in, pull-based, multiple-stakeholder, multiple-scale, high-automation, agile methodology.」
翻成白話:BDD 是一個從外到內、多方利害關係人參與的敏捷方法論。重點在「多方利害關係人參與」,不在 Given/When/Then 語法
什麼時候用什麼
既然三者在程式碼層面本質都一樣,那選擇就不是「TDD vs BDD vs ATDD」。真正該問的問題只有兩個
| 問題 | 答案 | 做法 |
|---|---|---|
| 需不需要跟非技術人員溝通需求? | 需要 → 用 SBE | Three Amigos 會議 + Gherkin 規格文件,讓 PM、QA、開發一起把需求釘死 |
| 不需要 → 直接 TDD | 不用多一層 Cucumber 抽象,describe/it 就夠了 | |
| 測試該寫在哪個層級? | 單元 / 整合 / 端到端 | 這跟方法論無關。TDD 本身就可以在任何層級運作,雙迴圈只是 TDD 在不同層級的應用 |
簡單來說:寫程式碼的時候,你做的都是 TDD。差別只在於需求怎麼來的。是開發者自己理解的,還是透過 SBE 跟利害關係人一起定義的
還有一個經驗法則:如果你的 Gherkin Feature 檔案只有開發者看得懂,那你可能不需要 Cucumber。直接用 describe/it 寫就好了,不用多一層抽象
最後一個常被忽略的點:SBE 不依賴任何測試框架。就算你的團隊完全不寫自動化測試(雖然不推薦),Three Amigos 會議和用具體例子釐清需求這件事本身就有價值。你可以把討論出來的 Scenario 寫在 wiki、寫在 Notion、甚至寫在白板上。BDD 是建構在 SBE 之上的 — 它把 SBE 產出的例子變成可執行的測試。但 SBE 本身是獨立的,它是一種溝通方式,不是一種測試方式
總結
講到這裡,我們可以歸納出四個重點
-
程式碼層面,TDD、BDD、ATDD 沒有本質差異。describe/it 就是 BDD 風格的語法,你可能已經在用了。在團隊會議上吵「要用 TDD 還是 BDD」之前,先確認大家說的是同一件事
-
ATDD 和 BDD 不需要區分,差異只在溝通方式。xUnit 風格 (describe + test) 和 BDD 風格 (describe + it) 做的是一模一樣的事情。ATDD 和 BDD 在驗收層級也幾乎可以互換。Cucumber/Gherkin 是溝通工具,不是替代 TDD 的東西
-
BDD 真正的價值在 SBE。Specification by Example 讓團隊在寫程式碼之前就把需求搞清楚。Three Amigos 會議比任何框架都重要
-
SBE 是獨立的溝通方式,BDD 是建構在它之上的。就算團隊不用 TDD、不用 BDD、不用任何自動化測試框架,SBE 的「用具體例子釐清需求」這件事本身就能帶來價值。BDD 只是把 SBE 的產出再往前推一步,變成可執行的測試
-
TDD 本身就不限於單元測試。雙迴圈 (驗收測試在外、單元測試在內) 只是 TDD 在不同測試層級的應用,不需要另外取一個名字叫 ATDD
最後,引用 Dan North 的一段話作為結尾
「BDD 不是關於工具。它是關於跟利害關係人一起,用具體的例子來探索、發現、定義想要的系統行為」
不要被框架和語法迷惑了。先跟你的團隊把需求搞清楚,選什麼工具反而是最不重要的決定
圖片來源:AI 產生