# 關於 BDD 與 Gherkin > Behavior-Driven Development,行為驅動開發 BDD 是一種軟體開發方法論,從 TDD(測試驅動開發)演進而來。它強調透過「行為」來描述系統需求,關注軟體應該「做什麼」,而不只是「怎麼做」。 **核心理念:** - 以行為為中心,從用戶和業務需求的角度描述系統行為 - 讓 PM、RD、QA 使用同一種語言溝通 - 將需求直接轉換為可執行的測試 BDD 解決了傳統開發中 **「 需求翻譯 」** 的問題,讓所有角色對需求有一致的理解。 ## 什麼是 Gherkin? Gherkin 是實現 BDD 的一種具體的規格語言,可以這樣理解: - BDD = 開發方法論( 概念層面 ) - Gherkin = 描述語言( 實踐工具 ) ### Gherkin 的特性 1. 領域特定語言(DSL) -- 使用自然語言,但有明確的語法規則 -- 人類可讀,機器可執行 2. 結構化語法 ```gherkin= Feature: 功能描述 Rule: 業務規則 Scenario/Example: 測試場景 Given: 給定(前置條件) When: 當(執行操作) Then: 那麼(預期結果) And: 和(連接同類步驟) ``` 3. 可執行性 -- Gherkin 文件可被 Cucumber、SpecFlow、Behave 等工具解析 -- 連接到實際的測試代碼(Step Definitions) :::info Gherkin 核心關鍵字 | 關鍵字 | 用途 | 說明 | |--------|------|------| | **Feature** | 功能特性 | 描述要實現的功能模組 | | **Rule** | 業務規則 | 原子化的業務邏輯單元 | | **Scenario** | 具體場景 | 描述單一測試情境 | | **Scenario Outline** | 參數化場景 | 用表格覆蓋多組測資 | | **Given** | 前置條件 | 設定初始狀態 | | **When** | 執行動作 | 觸發的事件或操作 | | **Then** | 預期結果 | 驗證的輸出 | | **And / But** | 連接步驟 | 連接多個同類步驟 | | **Background** | 共用前置 | 所有場景共享的前置條件 | | **Examples** | 測試數據表 | 提供多組參數化數據 | ::: --- ## Gherkin 撰寫實務範例 ### **▶ 基礎層:Feature + Scenario(正例 + 反例)** > **適用時機:** 驗證單一功能的基本行為 ```gherkin= Feature: 查詢商品價格 使用者可以查詢系統中任一商品的當前價格 Scenario: 查詢已上架商品的價格(正例) Given 商品 "可樂" 存在於系統中,價格為 30 元 When 使用者查詢商品 "可樂" 的價格 Then 系統回傳價格為 30 元 Scenario: 查詢不存在的商品(反例) When 使用者查詢商品「不存在的商品」的價格 Then 操作失敗 ``` **撰寫要點:** - 使用雙引號「 `""` 」標記參數 - 數字可以直接使用,無需引號 - 反例的 Given 可以省略(無前置條件時) - 應包含正例與反例 <br /> --- ### **▶ 中階層:Feature + Rule + Scenario(原子化規則)** > 適用時機:功能包含多條業務規則 > ```gherkin= Feature: 計算訂單折扣 Rule: 滿額折價 當單筆訂單金額達到門檻時,系統應給予相應折扣 Example: 訂單金額達到折扣門檻 Given 系統設定滿 1000 元折 100 元 And 購物車中有商品總額為 1200 元 When 使用者結帳 Then 折扣金額為 100 元 And 最終金額為 1100 元 Example: 訂單金額未達折扣門檻 Given 系統設定滿 1000 元折 100 元 And 購物車中有商品總額為 800 元 When 使用者結帳 Then 折扣金額為 0 元 And 最終金額為 800 元 ``` **原子化原則:** - 每條 Rule 是一個不可分割的業務邏輯單元 - 每個 Feature 對應開發中要實作的一個邏輯模組 - Rule 幫助拆解複雜功能為獨立規則 <br /> --- ### **▶ 進階層:Feature + Rule + Scenario Outline + Examples** > 適用時機:需要驗證多組測資和邊界條件 ```gherkin= Feature: 會員等級折扣計算 Rule: 不同會員等級享有不同折扣率 系統根據會員等級自動計算折扣價格 Scenario Outline: 查詢商品價格並套用會員折扣 Given 商品 "可樂" 原價為 30 元 And 會員「<會員名稱>」的等級為「<會員等級>」 When 會員「<會員名稱>」查詢商品 "可樂" 的價格 Then 系統回傳折扣後價格為 <折扣價> 元 Examples: 一般場景 | 會員名稱 | 會員等級 | 折扣價 | | 小明 | 一般會員 | 30 | | 小華 | 銀卡會員 | 27 | | 小李 | 金卡會員 | 24 | Examples: 邊界場景 | 會員名稱 | 會員等級 | 折扣價 | 備註 | | 小王 | 白金會員 | 21 | 最高折扣 | | 小陳 | 未登入 | 30 | 未登入無折扣 | | 小張 | 黑名單 | 錯誤 | 黑名單禁止查詢價格 | ``` **優勢:** - 避免重複撰寫相似場景 - 清楚呈現不同輸入與輸出的關係 - 可分組測試數據(一般場景 vs 邊界場景) <br /> ## Gherkin 進階技巧 **▶ Background:提煉共用前置步驟** > 適用時機:多個場景有相同的前置條件 ```gherkin! Feature: 會員下訂單 會員必須先登入,才能將商品加入購物車並下訂 Background: Given 會員 "小明" 已註冊,帳號為「ming@example.com」 And 會員 "小明" 已登入系統 And 商品 "可樂" 存在於系統中,價格為 30 元 And 商品 "洋芋片" 存在於系統中,價格為 50 元 Scenario: 會員下訂單包含單一商品 Given 會員 "小明" 將商品 "可樂" 加入購物車,數量為 2 When 會員 "小明" 下定訂單 Then 訂單的訂單金額總額為 60 元 And 訂單的擁有者為會員 "小明" Scenario: 會員下訂單包含多項商品 Given 會員 "小明" 將商品 "可樂" 加入購物車,數量為 3 And 會員 "小明" 將商品 "洋芋片" 加入購物車,數量為 2 When 會員 "小明" 下定訂單 Then 訂單的訂單金額總額為 190 元 And 訂單的擁有者為會員 "小明" ``` **注意事項:** - Background 會在每個 Scenario 執行前運行 - 只放共用的前置條件,不要放測試邏輯 --- ### **▶ Docstring:傳遞複雜結構** > 適用時機:需要傳遞 JSON、XML、Markdown 等多行文本 ```gherkin= Scenario: 建立會員資料 Given 系統接收到以下 JSON 資料 """ { "name": "小明", "email": "ming@example.com", "preferences": { "newsletter": true, "notifications": false } } """ When 建立新會員 Then 會員資料儲存成功 ``` **格式規則:** - 使用三個雙引號 """ 包裹 - 保持原始格式(包括縮排和換行) --- ### **▶ Data Table:指定批次參數** > 適用時機:需要一次性設定多筆結構化資料 ```gherkin= Scenario: 批次新增商品 Given 系統中有以下商品 | 商品名稱 | 價格 | 庫存 | | 可樂 | 30 | 100 | | 洋芋片 | 50 | 50 | | 礦泉水 | 20 | 200 | When 使用者查詢所有商品 Then 系統回傳 3 筆商品資料 ``` **與 Examples 的區別:** - Data Table:在單一步驟中傳遞多筆資料 - Examples:定義多組完整的場景參數 --- ### **▶ 使用星號(*)取代關鍵字 (And)** ```gherkin= Scenario: 完成購物 Given 我正在購物 * 我有雞蛋 * 我有牛奶 * 我有奶油 When 我檢查清單 Then 我不需要其他東西 ``` **何時使用:** - 多個連續的 And 步驟 - 列舉類型的步驟 - 強調步驟的並列關係 <br /> ## 如何選擇合適的架構? **問題一:這個功能有多條業務規則需要處理嗎?** - 否 → 使用 Feature + Scenario - 是 → 使用 Feature + Rule + Scenario **問題二:同一條規則需要驗證多組測資嗎?** - 否 → 維持 Scenario - 是 → 升級 Scenario Outline + Examples **問題三:多個場景有相同的前置步驟嗎?** - 是 → 加入 Background ``` 你的功能有多條業務規則嗎? ├─ 否 → 使用【基礎層】Feature + Scenario └─ 是 → 使用【中階層】Feature + Rule + Scenario │ └─ 需要驗證多組測資嗎? ├─ 否 → 維持 Scenario └─ 是 → 升級【進階層】Scenario Outline + Examples 有重複的前置步驟嗎? └─ 是 → 加入 Background 需要批次資料嗎? └─ 是 → 使用 Data Table 需要傳遞複雜結構(JSON/XML)嗎? └─ 是 → 使用 Docstring ``` ## BDD 實踐 ### 實踐流程 ``` 1. 三方會議(Three Amigos) PM + RD + QA 共同討論需求 ↓ 2. 用 Gherkin 撰寫場景 將需求轉換為 Given-When-Then 格式 ↓ 3. 開發 Step Definitions 將 Gherkin 步驟連接到實際代碼 ↓ 4. 執行測試 Cucumber/SpecFlow/Behave 執行測試 ↓ 5. 持續更新 需求變更時同步更新 Gherkin 文件 ``` ### 撰寫原則 #### 1. 使用業務語言,避免技術細節 -- ❌ `Given 資料庫中 users 表有一筆 id=1 的記錄` -- ✅ `Given 會員 "小明" 已註冊` #### 2. 每個 Scenario 保持獨立 -- 不依賴其他場景的執行結果 -- 可以任意順序執行 #### 3. 一個 Scenario 只驗證一件事 -- 避免過長的場景 -- 拆分複雜邏輯為多個 Rule #### 4. 善用參數化 -- 相似場景使用 Scenario Outline -- 減少重複代碼 #### 5. 保持 Step 的原子性 -- 每個步驟做一件事 -- 便於重用和維護 #### ※ 常見陷阱 - 過度技術化 - Gherkin 應該讓非技術人員也能理解 - 步驟耦合 - Given/When/Then 步驟互相依賴內部狀態 - 缺乏具體性 - 使用模糊的詞彙如「正確的」「適當的」 - 過度使用 Background - 導致場景難以獨立理解 <br /> ## 總結 - BDD 是一種開發方法論,強調用行為描述需求 - Gherkin 是實現 BDD 的標準化語言 - 兩者的關係:沒有 BDD,Gherkin 只是語法;沒有 Gherkin,BDD 難以標準化實踐 - 選擇合適的架構層次(基礎/中階/進階)取決於功能複雜度 - 善用進階技巧(Background、Data Table、Docstring)提升可維護性 - BDD 的核心價值在於讓所有角色對需求有一致的理解