Behaviour-Driven Development for your team

新工作 4 個月以來接觸 E2E 測試與 BDD 的心得

前言

紀錄 4 個月來寫測試產生的疑問與解答、個人經歷、測試的要點與技巧這類語法之上更高層次的問題,以及讓我的團隊對導入 BDD 有更大的信心與認知。

近期加入新工作期間內除了前端開發也兼職測試,撰寫了近百支大大小小的 E2E 測試,打算透過邊撰寫本篇文章邊查閱文件穩固知識,同時也記錄下來方便團隊成員能夠快速上手新建立的 Cypress E2E 測試專案,並且最終期望能夠嘗試實驗導入 BDD 流程到團隊開發流程當中。

第一次與 BDD 以及 E2E 測試見面

會接觸 BDD 是因為舊有專案有在嘗試導入該開發流程,不過隨著人員更動舊有的專案也逐漸棄用,而加入團隊第一件事就是建立新的 E2E 測試專案穩固陳舊代碼並且加減摸索期望導入 BDD 流程「增進開發產品的效率」與「提高對產品修改時的信心」。

前數個月都是以「透過熟悉現有產品代碼與行為」作為出發點開始,過程中順便翻閱文件邊學邊寫測試,在舊有的專案中則是使用了 Cypress🔗Cucumber🔗

E2E?Cypress?BDD?Cucumber?這些名詞對當時的我來說都是陌生的,只想趕快專案快速建起來再慢慢補足理論知識,現在要用一句話總結我會這樣描述它們:

  • End-To-End 測試:透過模擬真實用戶在應用程式中的操作,以達成驗證產品功能與可靠性的目的。
  • Cypress:主要用於 E2E 測試的 JavaScript 框架,用於撰寫測試代碼自動化模擬用戶操作。
  • Behaviour-Driven Development 行為驅動開發:一種著重於系統期望的行為的軟體開發模式,透過手段像是:鼓勵商業團隊與開發團隊間相互合作、組織商業可讀的規格文件、將規格文件與測試代碼連動自動化驗證行為是否符合預期……等,來達成目的。
  • Cucumber:用於將商業可讀的規格文件(Gherkin 語法)轉換成可執行的測試代碼的 BDD 工具。

團隊合作

既然商業團隊與開發團隊都被鼓勵參與到 BDD 的流程當中,而 BDD 又將測試作為軟體開發其中不可分割的驗證手端,那麼了解一些好測試的基本原則對各方都是有幫助的!如果你認同從產品行為去定義開發產品,以下幾個手段與原則或許有幫助:

鼓勵商業團隊與開發團隊合作

最常見的手段就是「三個朋友 Three Amigos」會議,透過聚集 3 方不同視角的人員一同討論(重點是不同視角不是人數)

  • 💹 商業人員 - 期望解決什麼問題?
  • 👨🏻‍💻 開發人員 - 如何建構解方解決問題?
  • 🧪 測試人員 - 有沒有可能發生 x 狀況?

在商業團隊定義出產品的需求時可以依照以下的模板來敘述:身為 (某種用戶),需要使用 (某種功能),以便達成 (某種收益)。以下是 Cucumber School 的 BDD 教學範例🔗

身為 (銷售助理) 需要使用 (退款功能) 以便達成 (滿足用戶的法定權力)

雖然範例中開發者的並沒有太多發言,不過光是參與這場會議可以讓相關人士對需求的前因後果有更深入的了解,並且通常在詳細討論實踐細節時開發者更能窺見技術上的限制並與團隊一同討論出可行的解方。

1. 💹:任何產品只要用戶還留有發票就可以在 14 天之內退款。
2. 👨🏻‍💻:能提供一個實際案例嗎?
3. 💹:A 用戶購買了一個商品,但事後不太喜歡產品的顏色,所以她向銷售助理申請退款。
4. 🧪:所以我們期望達成什麼?有什麼是可以測試的?
5. 💹:我們需要增加該產品數量,因為用戶已經把賣出去的產品退回來了。
6. 🧪:好的,還有嗎?
7. 💹:我想就這樣?
8. 🧪:如果用戶沒有發票呢?銷售助理應該直接拒絕請求嗎?
9. 💹:因為管理者可以處理任何退款,只需要有任何購買證明……或許 A 用戶可以用銀行帳單來證明她的購買紀錄。但銷售助理沒有權限,所以他應該與他的上級溝通。
10. 🧪:好,所以有兩種規則,一種是用戶有發票,另一種是用戶沒有發票,而退款時沒有發票則需要上級授權,對吧?
11. 💹:對,我想就這樣。
12. 👨🏻‍💻:是不是用戶應該得到退款?
13. 🧪:以及……是不是系統需要通知倉庫回收退款產品?
14. 💹:好主意!我想這次的故事討論就到這邊。

在會議結束後,透過討論的內容編寫產品的行為文件須特別留意。

通常由單一角度像是單獨由商業團隊開出需求文件時,會出現許多偏差,撰寫出來的文件並不代表所有人的共識與理解,這些偏差可能會導致難以開發或自動化測試,而開發者為了能夠自動化這些場景著手編寫文件又會導致特殊的語法(如 Gherkin)成為開發團隊「使用的東西」,並且可能誤解真實意圖產出不正確的文件。

最好的做法可以是商業與開發雙方都要保持共識,透過「行為描述定義出活文件」並透過雙方共同審查維護。文件可以依照團隊偏好規則撰寫,最好團隊中任何人都能閱讀理解、自動化測試。

文件編寫

好測試的 3A 原則

任何測試的結構借鑑 Filip Hric🔗 所提到的 3A 原則其實就是三個簡單的步驟:

安排 - 行動 - 斷言
  1. 安排 (Arrange):準備測試的前置環境 - 已過去的事
  2. 行動 (Act):執行測試 - 測試要發生的事
  3. 斷言 (Assert):驗證測試的結果 - 未來的事

用一致的語言來描述產品的行為

為了避免溝通上的偏差,通常會導入某種制式化的語言來描述產品的行為,像是 Gherkin 語法,這種語法的好處是可以讓商業團隊與開發團隊都能夠用相同的語言來描述產品的行為,並且透過工具將這些語句對應測試代碼轉換成可執行測試的活文件。

Given 到達登入頁面
When 登入帳號密碼
Then 成功登入

以 Gherkin 語法為例,看似有許多關鍵字要學習,實際上所有關鍵字仍依照測試 3A 規則,圍繞在 GivenWhenThen 為這三種關鍵字功能做延伸,可以參考官方 Gherkin 文件🔗或者是這份超濃縮懶人包🔗,此外 Cucumber 的出發點是編寫商業可讀的活文件,可以透過在地化 Gherkin 語法🔗 確保每個團隊成員更用最有效率的方式快速進入狀況溝通與紀錄產品文件。

使用 BDD 並不意味著一定要使用 Cucumber 的 Gherkin 語法,但它是一個十分有效的工具用於實踐 BDD。

過多的實踐細節

著重在軟體的目標而不是機制 表達規格從問題出發而不是解方出發

操作程式會有許多與 UI 相關的互動細節,像是:「點擊特定連結」、「尋找特定元件」,這些描述是用戶期望的行為而是極為脆弱易變的實踐細節,所以在撰寫文件時應該避免這些細節,而是專注於用戶期望的行為。以下是實際案例:

@聲明式(Good)
場景: 驗證登入
假如 無登入用戶到達登入頁
輸入登入資訊並登入
那麼 顯示登入成功訊息
@命令式(Bad)
場景: 驗證登入
假如 無登入用戶到達登入頁
輸入用戶名
輸入密碼
點擊登入按鈕
那麼 顯示登入成功訊息

在文件中避免描述實踐細節不是偷懶!也不要感覺「既然都來了所以就都寫清楚」,假如未來登入的 UI 或資料有任何變動,那麼文件也須要修改。讓實踐的細節撰寫在測試當中,文件只描述意圖即可。

  1. 文件著重在意圖而非實踐細節
  2. 意圖相較於實踐細節更不常更動,也更能採納實踐上的變化
  3. 去除操作細節的文件將更好閱讀 (必須讓文件前置知識要求或閱讀成本降至越低越好)

這個問題時常發生在你是撰寫情境文件,同時又是提出解決方案或實踐測試功能的人,需要留意文件的本質在描述產品的行為而非實踐細節,這樣才能確保文件的可讀性與可維護性。

結語

軟體的開發模式是一個比較抽象廣大的主題,隨時歡迎連絡與我討論。這篇文章藉由我近期的經歷與資料蒐集來總結出一些我認為重要的觀念與團隊討論,並期望透過這些觀念來幫助所有產品團隊更有效率的開發產品。預期有多的東西要補充會再持續更新。

怎麼樣才算是 BDD?

「怎麼樣才算是 BDD?」這個問題反覆的出現在腦海中,用了 Cucumber 就算是?還是因為測試使用 Given-When-Then 語句做分類?還是跨領域討論一起編寫文件才算?最終我統整出了面對不同人的解釋:

  • 開發者
    1. 開發者開始替程式撰寫測試
    2. 開發者發現隨著測試增加對程式的信心也隨之提高
    3. 開發者發現先寫測試再開發能夠幫助他們專注在必要編寫的程式上
    4. 開發者發現回顧一些舊代碼時測試能夠作為文件協助理解程式的行為
    5. 開發者接受測試可以作為開發文件
    6. 開發者發現 TDD 實際上是在定義行為而非測試
    7. Behaviour is about the interactions between components of the system and so the use of mocking is fundamental to advanced TDD. (暫且沒概念怎麼翻 😑)
    8. *我個人總結是:BDD 是 TDD 的團隊合作進階版
  • 普通人
    1. 鼓勵商業團隊與開發團隊間相互合作
    2. 組織商業可讀的規格文件
    3. 將規格文件與測試代碼連動自動化驗證行為是否符合預期

延伸閱讀