寫下第一個測試:從空測試檔案報錯,到紅燈綠燈重構,用 add 函式實際走一次完整 TDD 循環全紀錄
換一個新範例:基礎四則運算
講師 Steve Kinney 換到一個叫 basic-math 的資料夾,裡面有個 arithmetic 檔案,一開始裡面的函式都還沒有實作內容,對應的測試也是空的,什麼都沒驗證。
空測試檔案會讓測試執行器不開心
如果一個測試檔案裡完全沒有任何測試,測試執行器多半會回報錯誤提醒你這件事。Steve 實際把檔案內容全部註解掉,測試執行器立刻出現抱怨,因為這個檔案裡完全找不到任何測試可以執行。
如果暫時不想讓某個測試(或一整組測試)跑,可以用 .todo 或 .skip 標記:
javascript
it.skip('...', () => {
// 這個測試壞了,先跳過,之後回來修
});
it.todo('...');兩者效果上都是先不執行,但語意不同,是講給日後回頭看程式碼的人(也就是自己)聽的:.skip 代表這個測試目前壞掉了、還沒修好;.todo 代表這個測試之後一定會補上。.todo 在剛開始規劃測試、或幫別人搭好一個 boilerplate 專案架構時特別好用。用這兩種標記跑測試,測試執行器就不會抱怨,因為這些測試是「刻意」被跳過的。
測試檔案怎麼被找到
測試執行器基本上是尋找檔名符合 .test.js 或 .test.ts 這種命名規則的檔案來執行。有些做法會把測試集中放進一個獨立的 __test__ 目錄,但 Steve 偏好把測試檔案跟被測試的原始碼放在同一個目錄下,方便同時打開兩個檔案對照著看。
測試檔案的內容,就是把要測試的東西匯入進來。這裡有個值得留意的原則:盡量讓測試的目標只是單一一個函式,而不是被包在 React 的 Context API 或層層依賴裡、光是匯入就會觸發一堆 API 請求的東西。
課程後面也會談到另一種做法,就是直接把瀏覽器指向這個應用程式,實際跑起來測試整個流程。那種測試很有價值,因為它驗證的是所有東西真的串在一起能動,但也因為要啟動瀏覽器、等頁面載入、再操作 DOM,執行起來相對慢。相較之下,只測試單一函式的測試跑得快,能更快得到紅燈或綠燈的回饋,讓人能更快做出下一步的判斷。兩種測試各有適合的場景,怎麼分配比重取決於能不能有效降低改動程式碼時的焦慮感,而不是硬性規定的比例。
實際寫下第一個測試
先把要測試的函式匯入進來:
javascript
import { it, expect } from 'vitest';
import add from './arithmetic.js';
it('should add two positive numbers', () => {
expect(add(2, 2)).toBe(4);
});測試名稱本身的作用,是讓執行結果的清單能清楚顯示是哪一個測試失敗了。這時候執行測試會是紅燈,因為 add 函式還沒有任何實作內容,2 + 2 的結果不是 4,而是 undefined。
這正是第一篇提到測試驅動開發時只先帶過概念的「紅燈、綠燈、重構」節奏,這裡才真正走一次完整流程:測試一開始失敗,是因為函式本身還什麼都沒做。接著把函式實作補上:
javascript
export default function add(a, b) {
return a + b;
}測試立刻變成綠燈,這是很直接的成就感回饋。理論上下一步就會進入重構階段,在測試通過的前提下回頭整理、清理程式碼。像 add 這種函式本身沒什麼好重構的,但面對更複雜的實作時,只要每次存檔都能得到測試是否還通過的即時回饋,就能放心進行重構。
複習
測試驅動開發(TDD)的典型工作流程是什麼?
流程遵循「紅燈、綠燈、重構」的模式:先從一個會失敗的測試開始(紅燈),寫程式碼讓測試通過(綠燈),再在保持測試通過的前提下重構程式碼
撰寫簡單、聚焦的單元測試有什麼好處?
簡單的單元測試能提供快速的回饋,形成快速的迭代循環,讓開發者比起處理較慢、較複雜的整合測試或瀏覽器測試時,能更快做出決策
測試檔案通常採用什麼命名慣例?
測試檔案通常以 .test.js 或 .test.ts 作為副檔名,並且經常跟被測試的原始碼放在同一個目錄下
有什麼方式可以暫時跳過測試,或把測試標記為待完成?
測試執行器提供 .todo 或 .skip 方法,可以把測試標記為刻意跳過,或是計畫日後才實作
測試驅動開發(TDD)循環的三個階段是什麼?
紅燈、綠燈、重構。首先寫一個會失敗的測試(紅燈),接著寫出讓測試通過所需的最少程式碼(綠燈),最後在確保測試依然通過的前提下重構程式碼、改善結構
小測驗
測試驅動開發(TDD)的典型階段有哪些?
紅燈、綠燈、重構當一個測試檔案裡沒有任何測試時,會發生什麼事?
測試執行器通常會回報錯誤在測試套件裡,有哪兩種方式可以跳過測試?
.todo 與 .skip在一個基本的加法函式測試裡,是什麼原因讓測試一開始失敗?
函式回傳的是 undefined,而不是計算後的結果此文章是 FrontendMasters 上的 Testing Fundamentals 課程筆記
