獨孤九劍的「破氣式」是對付身具上乘內功敵人的劍招,其核心為「神而明之,存乎一心」的意念與境界,強調透過觀想來運用劍術。行為驅動開發(BDD)就像是軟體開發中的「破氣式」,它不只是單純的程式碼實作,更強調透過團隊溝通,讓大家對產品的行為達成共識,從根本上解決需求與產出不一致的問題。
BDD(Behavior-Driven Development,行為驅動開發)是一種結合需求溝通與自動化驗收測試的開發方法。它強調以使用者行為為中心,將需求用自然語言描述成可讀性高、跨部門可理解的測試案例,並透過自動化工具直接驗證這些行為是否如預期發生。
這就要回到軟體開發流程,傳統需求文件往往存在理解落差:通常 PRD 是由產品經理的描述,開發與產品經理可能會有不同解讀。另外測試人員可能根據自己的理解撰寫案例,結果與需求不符。這就會造成需求與最後的產出會有落差。使用BDD 透過自然語言 + 範例的方式,讓需求方、開發、測試三方在專案初期就能對行為達成共識,並且將這些描述轉化為自動化測試,避免「開發出來的功能與需求不一致」的情況。
還記得第二天打狗棒法的 Gherkin 語法就是希望能夠透過自然語言的描述,讓所有參與的人員都能夠在一開始就對齊需求,這樣不只能夠降低需求溝通的成本、並且重複來回修改的頻率也會減少,透過 Gherkin 撰寫的檔案(.feature
),也能夠被轉換成程式碼被執行。而用 Gherkin 撰寫的檔案,也變成大家共同溝通的文件,如果今天需求改變了,從 .feature
檔案到測試案例執行都會一起需要被更新,這樣就等同於同步文件,因為也可以稱為「活的需求規格(Live Docuemnt)」。
但BDD 雖然能提升團隊溝通效率,但也有其困難處與挑戰。BDD 案例的維護成本可能很高,特別是當應用程式 UI 經常變動時,甚至可能在撰寫 .feature 檔案太過細節或商業邏輯太難表達,如果每個功能專注於核心業務流程並且作為溝通的橋樑,其他實作細節能夠由單元測試或是整合測試去涵蓋可能是比較適合的測試策略。
在討論驗收測試案例時,我認為不應該直接使用 AI 來生成這些案例。這個階段的價值在於人與人之間的討論,也包含對於術語的共識,確保每個團隊成員(包括產品經理、開發人員和 QA)都對需求有共同的理解。如果跳過這個關鍵的溝通環節,即使生成了再多的測試案例,也可能是在驗證錯誤的行為。雖然 AI 不適合主導驗收測試案例的產生,但它能在這個過程中扮演一個重要的輔助角色。我們可以讓 AI 檢查已經撰寫好的案例,找出可能的遺漏範圍或邊界條件,從而確保需求的覆蓋度更完整。
一旦團隊成功對齊需求並產出最終的驗收測試案例後,後續的程式碼撰寫工作就可以交給效率工具。此時,無論是人工手動撰寫,還是使用 AI(例如 GitHub Copilot或 Claude Code )來產生測試程式碼,都是可行的。因為最困難也最重要的溝通與共識階段已經完成,剩下的只是實作程式碼。
讓我們在使用第二天的「使用者登入」為範例,再一次理解 BBD 的開發流程,這個流程會是:
作為一個已註冊的使用者,
我希望能透過輸入帳號與密碼登入系統,
以便存取我的個人首頁與專屬功能。
使用者輸入正確的帳號與密碼後,系統應該將使用者導向至首頁。
使用者應該能夠看到使用者頭像
Feature: 使用者登入系統
為了能進入系統首頁
身為一位已註冊的使用者
我希望在輸入正確帳號與密碼後能成功登入
Scenario: 成功登入
Given 使用者位於登入頁
When 使用者輸入正確帳號與密碼
And 送出登入表單
Then 系統應導向首頁
And 顯示使用者頭像
import { Given, When, Then } from '@cucumber/cucumber';
import { expect } from '@playwright/test';
import { page } from '../world'; // 共用 Playwright page 物件
Given('使用者位於登入頁', async () => {
await page.goto('https://your-app.example.com/login');
});
When('使用者輸入正確帳號與密碼', async () => {
await page.getByTestId('login-username').fill('user01');
await page.getByTestId('login-password').fill('pass123');
});
When('送出登入表單', async () => {
await page.getByTestId('login-submit').click();
});
Then('系統應導向首頁', async () => {
await expect(page).toHaveURL('https://your-app.example.com/home');
});
Then('顯示使用者頭像', async () => {
await expect(page.getByTestId('user-avatar')).toBeVisible();
});
npx bddgen && npx playwright test
BDD 檔案要能成為溝通的工具,當整個團隊都遵循下列這些原則:
使用一致的語言:團隊應建立專案用詞表(例如:「登入頁」、「首頁」),確保所有 BDD 案例的描述都使用相同的術語。這不僅能讓測試案例更易於閱讀和理解,也能減少團隊成員之間的溝通障礙。
專注於行為而非實作細節:BDD 案例的描述應該是以「使用者看得見的結果」為主,而不是 UI 內部的結構或程式碼邏輯。例如,寫當我點擊「註冊」按鈕,比寫當我點擊 class 為 ".btn-register" 的元素 要來得好,因為它更符合商業團隊的溝通語言,也比較容易讓非技術人員理解。
保持案例簡潔:每個 Scenario 應該只測試一個明確的行為。避免在同一個案例中混合多個驗證點或使用者行為。這樣做的好處是,當測試失敗時,你能立即知道是哪個特定功能出了問題。
善用 Background 區塊:如果多個 Scenario 都有相同的前置條件,例如「身為一個已登入的使用者」,你可以將這些重複步驟放在 Background 區塊中。這不僅能減少程式碼的重複,還能讓測試檔案更清晰、更易於閱讀,其實跟前面介紹的 fixture 的用途是類似的。
整合 CI/CD:將 BDD 測試納入持續整合流程是不可或缺的一步。每次程式碼部署時,自動執行 BDD 測試,可以確保新功能沒有破壞既有功能,並為團隊提供即時的回饋。
BDD 是一種以行為為中心的開發方法,將需求用自然語言轉化為可執行的測試案例,達到「需求即測試,測試即檔案」的效果。它不僅能幫助團隊溝通,更能確保每個功能符合使用者的真實需求。透過 Playwright 搭配 BDD 工具,團隊能將 BDD 案例落地到 E2E 測試,讓系統功能在開發與維護過程中保持正確與穩定。