🧪 測試驅動開發(TDD)
📖 定義
TDD(測試驅動開發)是一種先編寫測試,然後編寫程式碼以通過這些測試的開發方法。它遵循紅-綠-重構循環,提高程式碼品質,減少錯誤,使重構更安全。單元測試獨立驗證單個函數或元件。
🎯 簡單類比
先畫藍圖
傳統開發
1. 開始建房子
2. 完成後檢查
3. 發現問題 → 需要大修改
4. 成本增加
TDD
1. 畫藍圖(編寫測試)
2. 按藍圖建造(編寫程式碼)
3. 驗證(執行測試)
4. 改進(重構)
5. 安全準確
⚙️ 運作原理
TDD循環 (紅-綠-重構)
🔴 紅(失敗)
└─ 編寫測試 → 失敗(無程式碼)
🟢 綠(通過)
└─ 編寫最少程式碼 → 測試通過
🔵 重構( 改進)
└─ 改進程式碼 → 測試仍然通過
重複 → 逐步改進
💡 關鍵範例
基本TDD範例
// ========== 1. 紅: 編寫測試(失敗) ==========
test('add函數相加兩個數字', () => {
expect(add(2, 3)).toBe(5);
});
// FAIL - add未定義
// ========== 2. 綠: 最少程式碼(通過) ==========
function add(a, b) {
return a + b;
}
// PASS ✅
// ========== 3. 重構: 改進(如需要) ==========
// 程式碼簡單,無需改進
Jest單元測試
// user.js
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
isValidEmail() {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(this.email);
}
}
// user.test.js
describe('User', () => {
let user;
beforeEach(() => {
user = new User('張三', 'zhang@example.com');
});
test('有效信箱應回傳true', () => {
expect(user.isValidEmail()).toBe(true);
});
test('無效信箱應回傳false', () => {
user.email = 'invalid-email';
expect(user.isValidEmail()).toBe(false);
});
});
非同步測試
test('非同步函數返回資料', async () => {
const data = await fetchData();
expect(data).toEqual({ name: 'John' });
});
模擬
// 函數模擬
const mockCallback = jest.fn(x => x * 2);
[1, 2, 3].forEach(mockCallback);
expect(mockCallback).toHaveBeenCalledTimes(3);
// 模組模擬
jest.mock('axios');
test('fetchUser返回使用者資料', async () => {
axios.get.mockResolvedValue({ data: { id: 1 } });
const user = await fetchUser(1);
expect(user).toEqual({ id: 1 });
});
🤔 常見問題
Q1. TDD的優點?
A:
優點:
1. 減少錯誤
2. 安全重構
3. 程式碼品質提高
4. 文件化
5. 信心
缺點:
1. 初始時間投資
2. 學習曲線
3. 測試維護
結論: 長期收益
Q2. 應該測試什麼?
A:
// ✅ 應該測試
1. 業務邏輯
2. 邊緣情況
3. 錯誤處理
4. 公共API
// ❌ 不需要測試
1. 外部函式庫
2. 簡單getter/setter
3. 私有實作細節
Q3. 測試覆蓋率目標?
A:
# 測量覆蓋率
npm test -- --coverage
# 目標:
80%以上覆蓋率 // 現實目標
100%覆蓋率 // 理想但不現實
# 記住:
高覆蓋率 ≠ 好測試
有意義的測試很重要!
🎬 總結
TDD是軟體品質的基礎:
- 紅-綠-重構: TDD核心循環
- 單元測試: 快速且隔離
- 測試優先: 測試驅動設計
- 持續改進: 重構的安全網
測試是對未來自己的投資! 🧪✨