上面一篇談完好的範例的特性,借下來我們要利用一些範例,來說明撰寫範例時要注意的事項,希望透夠實際的案例,能夠讓讀者更有感覺。
(1) 說明
以下有個用戶故事以及相關的驗收條件,你覺得哪些驗收條件可以移除?
用戶故事
As a user, I want to use this Application, so I need to login successfully
驗收條件
A. 在登入畫面, 我們可以看到姓名/密碼欄位
B. 輸入正確的 id, 和密碼, 系統顯示可登入成功
C. 輸入正確 id/密碼 (David, 12345), 系統顯示可以登入成功
D. 輸入正確 id/密碼(大衛, bcc), 系統顯示可以登入成功
(2) 答案
這裡我們想要聚焦「有效果、不重複」。所以我們要留下的應該是「彼此有差異、涵蓋不同行為路徑」的範例:
(1) 說明
以下有個用戶故事以及相關的驗收條件,你覺得這些驗收條件有什麼問題?
用戶故事
身為一個用戶, 我想要管理我的客戶資料
驗收條件
A. 客戶的資料中不能包含特殊符號
B. 錯誤訊息不能顯示內部代碼
C. 系統不能失敗
D. 要確認資料量和欄位格式是否 MS SQL 能處理
(2) 答案
這一題的核心目標是幫助學員學會分辨「不良的驗收條件」,尤其是下列幾種常見錯誤類型:
• 模糊語意(ambiguous wording)
使用容易引起不同人不同解讀的詞語,造成需求無法被明確實作或測試。你常常會看到會有以下關鍵詞出現:
正常顯示
顯示正確資料
快速載入
使用者可以方便地操作
系統需穩定運作
• 無法測試(not testable)
無法用觀察、輸入或自動測試方式來驗證該條件是否成立。常見情境有:
未定義輸入 → 沒辦法知道什麼時候會觸發
未定義預期輸出 → 不知道正確結果是什麼
• 涉及實作細節(implementation concern)
驗收條件裡面出現了程式語言、資料庫語法、框架名稱等技術細節,讓需求與實作綁在一起。從行為導向的驗收條件,轉成技術限制導向的描述。
A:客戶的資料中不能包含特殊符號
問題分析:
• 「特殊符號」不具體,無法驗證
• 沒有提到在哪個欄位?什麼操作觸發驗收?
• 是資料匯入?還是使用者輸入?
改寫建議:
Scenario: 使用者輸入含特殊符號的姓名欄位
Given 我在「新增客戶」畫面
When 我輸入姓名為 "@王小明"
And 我提交表單
Then 系統應該顯示錯誤訊息:「姓名不能包含特殊符號」
B:顯示的資訊不能顯示內部代碼
問題分析:
• 「內部代碼」是什麼?是錯誤訊息?還是系統欄位?
• 沒有具體操作脈絡,難以重現
• 測試者或開發者很可能各自解讀不同(ex: 顯示 SQL 錯誤?ID?GUID?)
改寫建議:
Scenario: 系統錯誤時不應顯示內部系統碼
Given 使用者在儲存表單時系統發生錯誤
When 系統顯示錯誤訊息
Then 系統應該只顯示使用者可理解的訊息(如「系統發生問題,請聯絡客服」)
And 不應顯示如「Exception: NullPointer at Line 43」或錯誤代碼如 ERR-5023
C:不能失敗
問題分析:
• 是「希望」不是「可驗證條件」
• 沒有前提、行動與結果
• 也無法測試或定義什麼叫「失敗」
改寫建議:
你必須先釐清「誰不能失敗」「在什麼時候不能失敗」:
Scenario: 資料匯入過程成功完成
Given 我上傳正確格式的 CSV 客戶資料檔
When 我啟動資料匯入程序
Then 系統應該完成資料匯入
And 顯示訊息:「共匯入 120 筆成功,0 筆失敗」
或:
Scenario: 匯入錯誤資料時,系統應提示錯誤
Given 我上傳包含錯誤格式的 CSV 檔案
When 我啟動資料匯入
Then 系統應提示「第 3 筆資料格式錯誤」
D:要確認數量和欄位格式是否符合 MS SQL 能處理
問題分析:
• 太技術導向:驗收條件不應關心是否「MS SQL 可處理」
• 開發細節會變動(ex: 改用 PostgreSQL 就要改條件?)
• 缺乏前提與輸入例子
改寫建議:
你應該關心的是「輸入資料的格式正確,系統能處理」,而非後端 DB 能不能吃:
Scenario: 客戶年齡欄位不得超過 3 位數
Given 我在新增客戶畫面
When 我輸入年齡為 1234
And 我提交表單
Then 系統應提示錯誤:「年齡不可超過 3 位數」
Scenario: 客戶地址長度不得超過 255 字元
Given 我在輸入地址欄位時
When 我輸入超過 255 個字元的地址
Then 系統應提示錯誤:「地址欄位長度超過上限」
(1) 說明
以下是一個驗收條件的描述,你覺得這個驗收條件的標題可以叫做什麼?
Given a user orders an item of USD
When the order is dispatched
Then the selected shipping method should be
Cost | shipping
1 | post (平件)
10 | post (平件)
50 | courier (急件)
100 | courier (急件)
A: 商品運送
B: 根據訂單金額選擇運送方式
C: 高價商品應使用安全運送方式
D: 價格超過 50 美元的商品應使用快遞,而非平信
(2) 答案
這一題的真正目的,不只是幫驗收條件命名,而是要讓學員理解:
「如果驗收條件沒有標題,你其實很難從裡面的例子或規則倒推出原始的需求是什麼!」
從原先的驗收條件內容,只是顯示不同商品價格會有不同運送方式。但是不是價格超過 50 美元的商品應使用急件,而非平件,這我們無法判斷,我們最多只能選擇 B和C。
在案例一中的驗收條件B,雖然沒有具體的輸入或輸出值,通常我會留下來,不過我會改的比較完成,比較是接近需求的描述。而答案 C 和D 只是需求的其中一種可能和說明,有 C 和 D 會讓需求更清楚,但是沒有B,我們可能無法回推需求是什麼。所以有具體值得範例我們需要,能夠說明需求是什麼的範例我們也需要。
(1) 說明
以下有個用戶故事以及相關的驗收條件,你覺得哪個驗收條件比較好?
Given the purchase order contains
When user checks out
Then the discount offered should be
(A)
Items | discount
< 5 | 0%
5<=x<10 | 10%
10<=x<20 | 15%
=20 | 20%
(B)
Items | discount
3 | 0%
7 | 10%
15 | 15%
100 | 20%
(C)
Items | discount
1 | 0%
5 | 10%
9 | 10%
10 | 15%
19 | 15%
20 | 20%
21 | 20%
(2) 答案
這題想要看的重點,和範例三相同。驗收條件 A 他其實是需求的說明,而驗收條件 B 和 C 是需求的其中一個擁有具體數值的範例。兩者都是要有。
你需要驗收條件 B 和 C,在執行測試或是撰寫程式碼時,這可以直接應用。驗收條件可以是為註解,當作驗收條件的說明部分。萬一之後需要再增加測試案例或是驗收條件,可以根據這個需求說明再增加。
(1) 說明
以下針對同一個用戶故事有左右兩個驗收條件,你覺得哪個驗收條件比較好?
(2) 答案
在這一張投影片中,我們看到了一組使用者註冊的範例,但它分為了左右兩個版本。
左邊是實作導向,右邊是需求導向
左邊的敘述方式很像是要告訴前端工程師怎麼實作:欄位有哪些、按鈕叫什麼、順序為何。這類描述當然在 UI 設計或手動測試中有其用途,但在進行需求討論或用於範例設計時,反而會模糊焦點。因為 UI 是經常變動的,而需求的意圖卻常常是穩定的。
相對來說,右邊的版本把焦點放在「我們要幫使用者完成什麼任務?」這個問題上。從需求的角度看,PM 或產品設計師真正關心的不是有沒有填 confirm password,而是:使用者是否能夠成功完成註冊,系統有沒有讓他知道註冊完成,並進入他的專屬頁面。這樣的表述可以讓需求被跨角色、跨技術語言地理解與討論。
自動化測試時的維護痛點:左邊描述法容易失效
再進一步,如果團隊後續要把這個驗收條件用於自動化測試,就會明顯看出差異。
假設你根據左邊的描述撰寫測試腳本,可能會長成這樣:
When I visit "/home"
And I click "Sign up"
And I fill in "Username" with "Matt"
And I fill in "Password" with "pass123"
And I fill in "Confirm Password" with "pass123"
And I click "Register"
Then I should see "Hello, Matt"
這段測試看起來很完整,但它的問題在於:這些操作步驟都直接綁死在目前的 UI 結構上。如果畫面稍有調整,例如:
• 「Sign up」按鈕被放到側邊欄
• Password 改為兩階段填寫(多頁式表單)
• Confirm Password 改為 Email OTP 驗證
• 歡迎訊息位置被包進了另一個元件
那麼這段測試很可能就會「整段壞掉」,即使整體註冊流程與需求意圖沒有改變。我們只是換了一種方式實現它而已。
這種情況在實務中非常常見,特別是在做 UI 自動化測試時:畫面一改,測試大量失效,維護成本極高。而這些失效,並不是因為需求變了,而是因為測試寫得太貼近畫面操作細節。
反過來看,如果我們使用右邊這種「需求意圖導向」的方式撰寫驗收範例:
Scenario: Successful registration
Given the user completes registration
Then the system redirects to the homepage
And displays greeting message with the username
這段測試背後可以透過封裝來實作,例如在自動化框架中,我們可能寫一個 helper function complete_registration(),讓這段行為被封裝為一個「動作」,而不是一堆欄位填寫與點擊。當 UI 改變時,我們只需要改那個 helper 的實作,不需要改所有範例中的內容。
也就是說:範例內容不變,只改實作細節。這就是測試設計中的封裝原則,在 SBE 中一樣適用。它讓我們能更安心地修改介面、重構流程,而不會破壞原本的需求行為驗證。