Files
2025-11-29 18:03:43 +08:00

416 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
description: 讓測試通過Green 階段)。用最簡單、甚至是"錯誤"的方式快速讓測試變綠。
---
# TDD Green - 讓測試通過Green
用最簡單的方式讓測試通過。可以作弊、可以硬編碼、可以"錯誤"。
**【功能名】**{{feature_name}}
## Kent Beck 的 Green 階段理念
> "快速讓測試通過,用任何手段。"
> "乾淨的程式碼是目標,但先讓它能動。"
### 三種讓測試通過的策略
Kent Beck 在書中提出的三種策略:
#### 策略 1Fake It假實作
**最快速的方式:回傳常數**
```javascript
// 測試
test('5 * 2 = 10', () => {
expect(multiply(5, 2)).toBe(10);
});
// 假實作:直接回傳 10
function multiply(a, b) {
return 10;
}
```
**為什麼這樣做?**
- 快速得到綠燈
- 心理上的安全感
- 用下一個測試來逼出真正的實作
#### 策略 2Obvious Implementation明顯實作
**當實作很明顯時,直接寫出來**
```javascript
// 測試
test('加法運算', () => {
expect(add(2, 3)).toBe(5);
});
// 明顯實作:加法很簡單
function add(a, b) {
return a + b;
}
```
**何時使用?**
- 實作非常簡單
- 你很有信心
- 不需要三角測量
#### 策略 3Triangulation三角測量
**用多個測試來推導正確實作**
```javascript
// 第 1 個測試
test('1 + 1 = 2', () => {
expect(add(1, 1)).toBe(2);
});
// 假實作
function add(a, b) {
return 2;
}
// 第 2 個測試(三角測量)
test('2 + 3 = 5', () => {
expect(add(2, 3)).toBe(5);
});
// 被逼出真正的實作
function add(a, b) {
return a + b;
}
```
**何時使用?**
- 不確定正確的實作方式
- 想從多個角度驗證
- 設計還不清楚
## 實作步驟
### 1. 選擇策略
根據你的信心程度:
```
信心很低 → Fake It假實作
信心中等 → Triangulation三角測量
信心很高 → Obvious Implementation明顯實作
```
### 2. 寫最少的程式碼
**重點:最少**
```javascript
// 測試需要一個類別
test('Dollar 乘法', () => {
const five = new Dollar(5);
const product = five.times(2);
expect(product.amount).toBe(10);
});
// 最少的實作
class Dollar {
constructor(amount) {
this.amount = amount;
}
times(multiplier) {
return new Dollar(10); // 假實作!
}
}
```
### 3. 執行測試
```bash
npm test
```
**看到綠燈!** 🟢
### 4. 更新 journey.md
```markdown
#### 🟢 Green - 讓測試通過
策略:{Fake It / Obvious / Triangulation}
實作說明:
{簡述你做了什麼}
程式碼位置:{檔案路徑}
測試結果:✅ 通過
#### 🤔 反思
- 這個實作明顯是假的/硬編碼的嗎?
- 需要下一個測試來逼出真正的實作嗎?
- 有沒有重複的程式碼?
#### 📝 下一步
執行 /kb-refactor 重構
```
## Fake It 的威力
### 範例Money 的演進
**第 1 輪:完全假實作**
```javascript
test('5 * 2 = 10', () => {
const five = new Dollar(5);
expect(five.times(2).amount).toBe(10);
});
class Dollar {
constructor(amount) {
this.amount = amount;
}
times(multiplier) {
return new Dollar(10); // 硬編碼!
}
}
```
**第 2 輪:三角測量逼出真實作**
```javascript
test('5 * 3 = 15', () => {
const five = new Dollar(5);
expect(five.times(3).amount).toBe(15);
});
class Dollar {
times(multiplier) {
return new Dollar(this.amount * multiplier); // 真實作!
}
}
```
## 常見的"作弊"技巧
### 技巧 1回傳常數
```javascript
function getWelcomeMessage() {
return "Hello, World!"; // 先硬編碼
}
```
### 技巧 2複製測試資料
```javascript
// 測試
expect(processData(input)).toEqual({
status: 'success',
count: 5
});
// 實作:直接回傳期待值
function processData(input) {
return { status: 'success', count: 5 };
}
```
### 技巧 3最簡單的 if
```javascript
// 測試 1
test('even number', () => {
expect(classify(2)).toBe('even');
});
// 假實作
function classify(n) {
if (n === 2) return 'even';
}
// 測試 2 會逼出真實作
test('another even number', () => {
expect(classify(4)).toBe('even');
});
function classify(n) {
return n % 2 === 0 ? 'even' : 'odd';
}
```
## 實作的禁忌
Kent Beck 說明在 Green 階段應該避免的:
### ❌ 不要過度設計
```javascript
// ❌ 不要這樣(太複雜)
class Dollar {
constructor(amount, currency = 'USD') {
this.amount = amount;
this.currency = currency;
}
times(multiplier) {
// 處理多幣別、匯率轉換...
}
// 一堆還不需要的方法
add() {}
subtract() {}
convert() {}
}
// ✅ 要這樣(夠用就好)
class Dollar {
constructor(amount) {
this.amount = amount;
}
times(multiplier) {
return new Dollar(this.amount * multiplier);
}
}
```
### ❌ 不要一次實作多個測試
```javascript
// 目前只有一個測試需要 times()
// 不要順便實作 add(), subtract()
// 等有測試需要時再加
```
### ❌ 不要重構
```javascript
// ❌ Green 階段不要重構
// 先讓測試通過
// 重構留給下一步
// ✅ 先這樣
function calculate(x) {
return x * 2;
}
// 不要在這裡就改成
const MULTIPLIER = 2;
function calculate(x) {
return x * MULTIPLIER;
}
// 重構留給 /kb-refactor
```
## 心態:接受"醜陋"的程式碼
Kent Beck 強調的心態轉變:
```
階段 1讓它能動 (Make it work) ← 現在在這裡
階段 2讓它正確 (Make it right)
階段 3讓它快速 (Make it fast)
```
**Green 階段只追求"能動"**
- 硬編碼?沒關係
- 重複?沒關係
- 醜陋?沒關係
**下一步會改善**
## 範例完整流程
### 測試(失敗)
```javascript
test('購物車初始總價為 0', () => {
const cart = new ShoppingCart();
expect(cart.getTotal()).toBe(0);
});
```
### 實作策略選擇
**信心評估**:這個很簡單,用 Obvious Implementation
### 實作程式碼
```javascript
// src/shopping-cart.js
class ShoppingCart {
getTotal() {
return 0; // 最簡單的實作
}
}
module.exports = ShoppingCart;
```
### 執行測試
```bash
npm test
# ✅ 通過!
```
### 記錄到 journey.md
```markdown
#### 🟢 Green - 讓測試通過
策略Obvious Implementation
實作說明:
建立 ShoppingCart 類別getTotal() 回傳 0。
雖然是硬編碼,但足以通過目前的測試。
程式碼位置src/shopping-cart.js
測試結果:✅ 通過
#### 🤔 反思
- 實作很簡單,是硬編碼
- 下一個測試:加入商品後總價應該改變
- 目前沒有明顯重複
#### 📝 下一步
執行 /kb-refactor 檢查是否需要重構
```
## 速度的重要性
Kent Beck**Green 階段要快**
**目標**
- 幾秒鐘到幾分鐘
- 不要花太久時間
- 快速得到綠燈的心理回饋
**如果卡住**
- 寫更小的測試
- 用更假的實作
- 回退重來
## 下一步
測試通過了?執行:
```
/kb-refactor
```
檢查是否有重複,進行重構!
## 記住 Kent Beck 的話
> "讓測試通過的技巧是暫時降低對程式碼品質的標準。"
> "骯髒的程式碼是通往乾淨程式碼的墊腳石。"
> "當你看到綠燈時,那是重構的信號。"