簡單測試練習:用減法乘法除法練出紅燈綠燈節奏與測試肌肉記憶,也看清 TDD 並非任何情境都適用的萬用公式
練習的重點不是數學,是節奏
延續前一篇用 add 函式完整走過一次紅燈、綠燈、重構的流程,這篇接著練習減法、乘法、除法。Steve 強調這幾個函式本身沒有任何燒腦的實作邏輯,用數學運算子就能解決,練習真正的目的是把「拉進一個函式、看它失敗、跑測試、修改程式碼、再確認一次」這套節奏內化成身體反應,之後遇到更複雜的情境時,這套節奏已經是不需要多想的肌肉記憶。
減法:一樣先紅燈,再讓它變綠
減法函式一開始跟前一篇的 add 一樣,還沒有任何實作內容,所以第一次執行測試(it('should subtract one number from the other', ...))會是紅燈,因為函式本身還沒做任何事。補上實作後,函式回傳 a - b,測試立刻轉綠:
javascript
export default function subtract(a, b) {
return a - b;
}乘法:具體帶入數字,重複同一套節奏
乘法測試同樣先讓它紅燈,再補上實作。Steve 示範時用 3 乘以 2 應該等於 6 當作具體案例:
javascript
it('should multiply two numbers', () => {
expect(multiply(3, 2)).toBe(6);
});javascript
export default function multiply(a, b) {
return a * b;
}補上 return a * b 之後,測試同樣從紅燈變成綠燈,跟減法、加法走的是一模一樣的流程。
除法:先讓測試過,邊界案例之後再處理
除法也依循同樣的節奏,先寫測試、看它失敗,再補上 a / b 的實作讓它通過。Steve 特別提到除法比加減乘更容易出狀況,因為 JavaScript 處理浮點數運算時有自己的一套怪脾氣,但這篇練習的重點是先把測試寫成綠燈,那些浮點數相關的邊界案例會留到後面才處理,不在這裡一次解決。
這就是簡化版的測試驅動開發
這套「先寫一個會失敗的測試,再寫最少的程式碼讓它通過」的節奏,就是測試驅動開發最基礎的樣子。實際專案裡的情境當然會複雜得多,但這個模式特別適合用在有明確輸入輸出的情境,例如從一個資料結構裡取出特定屬性,或是把一種資料結構轉換成另一種、需要各自用不同方式運算的情況。
在這種情境下,這套流程能讓人很容易進入心流狀態,一直維持著寫測試、看綠燈的節奏往前推進。
為什麼不是隨時都能做測試驅動開發
如果這套流程這麼順手,為什麼不是每次寫程式碼都照著做?Steve 點出幾個常見的現實限制:
- 接手沒有測試的既有程式碼:測試驅動開發假設的是從零開始寫新程式碼,但如果程式碼早就寫好了,只是要在上面加一個小功能,就很難照著「先寫測試、再寫程式碼」的順序進行
- 還沒完全搞清楚問題是什麼:理想情況下,拿到一份 bug report,會先寫一個會失敗的測試來重現問題,再想辦法讓它通過。但有時候連問題本身是什麼都還不清楚,只能一邊摸索一邊嘗試,往往寫到六成才發現自己根本沒有照測試驅動開發的方式進行
這些限制呼應了系列一開始提到的「有些程式碼很難測試」,測試驅動開發是一個理想的工作節奏,但不是任何情境都適用的萬用公式,重點依然是有沒有實際降低改動程式碼時的焦慮感。
複習
在這個脈絡下,練習測試驅動開發(TDD)的主要目的是什麼?
建立起先寫測試、看它失敗,再寫程式碼讓測試通過的工作流程,藉此養成良好的開發習慣與肌肉記憶
這裡描述的測試驅動開發基本節奏是什麼?
寫一個會失敗的測試,實作最少量的程式碼讓測試通過,需要的話再進行重構
實踐測試驅動開發時會遇到哪些挑戰?
接手沒有測試的既有程式碼、還沒完全搞清楚問題是什麼,以及有時候必須先摸索嘗試各種做法,才能確定該怎麼解決
為什麼在開發流程早期就寫測試被認為是有價值的?
它能建立一致的工作流程、提供即時回饋,並確保程式碼在實作完成前就符合預期的需求
測試驅動開發(TDD)的基本節奏是什麼?
先寫一個會失敗的測試(紅燈),寫程式碼讓它通過(綠燈),需要的話再重構。整個循環包含拉進要測試的功能、看它失敗、執行測試、修改程式碼,然後重複這個過程
小測驗
實踐測試驅動開發時,展示的典型工作流程是什麼?
先寫一個會失敗的測試,再實作程式碼讓測試通過收到 bug report 時,建議採取的做法是什麼?
先寫一個會失敗的測試,再讓這個測試通過反覆練習測試驅動開發的主要好處是什麼?
培養撰寫測試的肌肉記憶下列何者是測試驅動開發可能難以套用的合理原因?
面對沒有任何測試的既有程式碼時此文章是 FrontendMasters 上的 Testing Fundamentals 課程筆記
