Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:03:43 +08:00
commit 2d7bed1e9d
8 changed files with 1725 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
{
"name": "kent-beck-tdd",
"description": "Kent Beck's organic TDD: Let design emerge naturally through small, iterative test cycles",
"version": "1.0.0",
"author": {
"name": "cash"
},
"commands": [
"./commands"
]
}

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# kent-beck-tdd
Kent Beck's organic TDD: Let design emerge naturally through small, iterative test cycles

415
commands/kb-green.md Normal file
View File

@@ -0,0 +1,415 @@
---
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 的話
> "讓測試通過的技巧是暫時降低對程式碼品質的標準。"
> "骯髒的程式碼是通往乾淨程式碼的墊腳石。"
> "當你看到綠燈時,那是重構的信號。"

274
commands/kb-red.md Normal file
View File

@@ -0,0 +1,274 @@
---
description: 寫下一個測試Red 階段)。一次只寫一個最小的測試,讓它失敗。
---
# TDD Red - 寫下一個測試Red
寫下一個小測試,看它失敗。
**【功能名】**{{feature_name}}
## Kent Beck 的 Red 階段理念
> "寫一個小測試。讓它失敗。"
### 一次一個測試的原則
**為什麼一次只寫一個?**
- 保持專注
- 快速反饋
- 小步前進
- 容易回退
### 如何選擇下一個測試?
Kent Beck 的建議:
1. **從 To-Do List 選**(心理 To-Do不是文件
- 腦中想到什麼測試,記下來
- 選最簡單的開始
2. **從上一個測試的經驗**
- 上一個測試通過了,下一步自然浮現
- "如果這樣,那會怎樣?"
3. **三角測量**
- 如果不確定實作,寫第二個類似的測試
- 從多個角度逼近正確實作
## 寫測試的步驟
### 1. 更新 journey.md
```markdown
### 第 N 輪 - {日期時間}
#### 🤔 想法
{為什麼要寫這個測試?從上一輪學到什麼?}
#### 🔴 Red - 寫測試
測試名稱:{測試名稱}
```
### 2. 寫測試程式碼
**測試結構Given-When-Then**
```javascript
test('簡短描述測試意圖', () => {
// Given - 準備
// 【準備】:{為什麼需要這些資料?}
const input = testData;
// When - 執行
// 【執行】:{測試什麼行為?}
const result = functionUnderTest(input);
// Then - 驗證
// 【驗證】:{為什麼期待這個結果?}
expect(result).toBe(expected);
});
```
### 3. 執行測試,確認失敗
```bash
npm test
```
**必須看到失敗!**
- 如果沒失敗,測試可能有問題
- 失敗訊息要清楚
### 4. 記錄失敗訊息
在 journey.md 中:
```markdown
#### 失敗訊息
```
{實際的錯誤訊息}
```
#### 📝 下一步
執行 /kb-green 讓測試通過
```
## 測試大小的藝術
### 測試要多小?
Kent Beck 的答案:**取決於你的信心**
**信心低(不確定)**
- 寫更小的測試
- 更頻繁的反饋
**信心高(很確定)**
- 可以寫大一點的測試
- 跨越明顯的步驟
### 範例:小測試 vs 大測試
**小測試(信心低時)**
```javascript
// 第 1 個測試:最基本的
test('5 元乘以 2 等於 10 元', () => {
const five = new Dollar(5);
const product = five.times(2);
expect(product.amount).toBe(10);
});
// 第 2 個測試:檢查副作用
test('乘法不改變原物件', () => {
const five = new Dollar(5);
five.times(2);
expect(five.amount).toBe(5); // 原值不變
});
```
**大測試(信心高時)**
```javascript
// 直接測試完整行為
test('Dollar 乘法運算', () => {
const five = new Dollar(5);
const ten = five.times(2);
expect(ten.amount).toBe(10);
expect(five.amount).toBe(5); // 也檢查副作用
});
```
## 三角測量的時機
當你不確定實作時,用三角測量:
```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;
}
```
## To-Do List 技巧
Kent Beck 建議在測試程式碼中寫註解:
```javascript
describe('Dollar', () => {
// TODO: 測試加法
// TODO: 測試負數
// TODO: 測試相等性
test('乘法', () => {
// 目前的測試
});
});
```
或在 journey.md 中:
```markdown
## 心理 To-Do List
- [x] 基本乘法
- [ ] 乘法副作用
- [ ] 加法運算
- [ ] 負數處理
```
## 常見問題
### Q: 要不要測試所有邊界情況?
Kent Beck**不用一開始就全測**
- 先從明顯的案例開始
- 邊界情況在需要時再加入
- 讓測試自然演進
### Q: 測試名稱要多詳細?
Kent Beck**描述意圖,不是實作**
```javascript
// ✅ 好的測試名稱
test('購物車加入商品後,總價增加')
// ❌ 不好的測試名稱
test('testAddItem')
```
### Q: 要寫測試到什麼程度?
Kent Beck**寫到你有信心為止**
- 如果還不確定,再寫一個測試
- 如果已經確定,可以跳到實作
## 範例Money 第一個測試
```javascript
// __tests__/money.test.js
describe('Dollar', () => {
test('乘法運算', () => {
// 【準備】:建立 5 元的 Dollar 物件
const five = new Dollar(5);
// 【執行】:乘以 2
const product = five.times(2);
// 【驗證】:結果應該是 10 元
expect(product.amount).toBe(10);
});
});
```
執行測試:
```bash
npm test
# 結果:失敗!
# Error: Dollar is not defined
```
在 journey.md 記錄:
```markdown
### 第 1 輪
#### 🔴 Red - 寫測試
測試名稱Dollar 乘法運算
#### 失敗訊息
```
Error: Dollar is not defined
```
#### 📝 下一步
執行 /kb-green 建立 Dollar 類別
```
## 下一步
測試寫好且失敗了?執行:
```
/kb-green
```
用最簡單的方式讓測試通過!
## 記住
> "測試不是目的,而是思考的工具。"
> "失敗的測試是進步的開始。"
> "一次一小步,但要持續前進。"

473
commands/kb-refactor.md Normal file
View File

@@ -0,0 +1,473 @@
---
description: 重構消除重複Refactor 階段)。在綠燈的保護下,改善程式碼品質。
---
# TDD Refactor - 重構消除重複Refactor
在測試通過後,消除重複、改善設計。測試是你的安全網。
**【功能名】**{{feature_name}}
## Kent Beck 的 Refactor 理念
> "重構的核心是消除重複。"
> "測試通過後,就是重構的時機。"
### 什麼是重複?
Kent Beck 定義的重複包括:
#### 1. 明顯的程式碼重複
```javascript
// ❌ 重複
function processOrderA(order) {
if (!order) throw new Error('Invalid order');
return order.total * 1.1;
}
function processOrderB(order) {
if (!order) throw new Error('Invalid order');
return order.total * 0.9;
}
// ✅ 消除重複
function validateOrder(order) {
if (!order) throw new Error('Invalid order');
}
function processOrderA(order) {
validateOrder(order);
return order.total * 1.1;
}
```
#### 2. 測試和實作之間的重複
```javascript
// 測試
test('5 * 2 = 10', () => {
expect(multiply(5, 2)).toBe(10);
});
// ❌ 實作中的重複(硬編碼)
function multiply(a, b) {
return 10; // 和測試中的 10 重複!
}
// ✅ 消除重複(真正的實作)
function multiply(a, b) {
return a * b;
}
```
#### 3. 概念上的重複
```javascript
// ❌ 概念重複(魔術數字)
function applyDiscount(price) {
return price * 0.9;
}
function calculateTax(price) {
return price * 0.1;
}
// ✅ 消除重複(提取常數)
const DISCOUNT_RATE = 0.9;
const TAX_RATE = 0.1;
function applyDiscount(price) {
return price * DISCOUNT_RATE;
}
function calculateTax(price) {
return price * TAX_RATE;
}
```
## 重構步驟
### 1. 確認測試通過
```bash
npm test
# 必須是綠燈!🟢
```
**如果是紅燈**
- 不要重構
- 先讓測試通過
### 2. 識別重複或異味
問自己:
- 有重複的程式碼嗎?
- 有硬編碼的值嗎?
- 有不清楚的命名嗎?
- 有太長的函式嗎?
- 有不必要的複雜度嗎?
### 3. 小步重構
**一次只改一個地方**
```
改一小步 → 執行測試 → 通過
改下一步 → 執行測試 → 通過
...
```
### 4. 每次都執行測試
**頻率**:每改一個地方就測試一次
```bash
# 改善命名
npm test
# 提取函式
npm test
# 提取常數
npm test
```
### 5. 如果測試失敗,立即回退
```bash
# 紅燈!
git checkout . # 回退
# 或手動 Undo
```
## 常見重構技巧
### 技巧 1消除硬編碼
**Before**
```javascript
class Dollar {
times(multiplier) {
return new Dollar(10); // 硬編碼
}
}
```
**After**
```javascript
class Dollar {
times(multiplier) {
return new Dollar(this.amount * multiplier);
}
}
```
### 技巧 2提取函式
**Before**
```javascript
function processOrder(order) {
// 驗證
if (!order.items || order.items.length === 0) {
throw new Error('No items');
}
// 計算
let total = 0;
for (const item of order.items) {
total += item.price * item.quantity;
}
return total;
}
```
**After**
```javascript
function processOrder(order) {
validateOrder(order);
return calculateTotal(order.items);
}
function validateOrder(order) {
if (!order.items || order.items.length === 0) {
throw new Error('No items');
}
}
function calculateTotal(items) {
let total = 0;
for (const item of items) {
total += item.price * item.quantity;
}
return total;
}
```
### 技巧 3提取常數
**Before**
```javascript
function calculateShipping(weight) {
if (weight > 1000) return 100;
return 50;
}
```
**After**
```javascript
const HEAVY_WEIGHT_THRESHOLD = 1000;
const SHIPPING_HEAVY = 100;
const SHIPPING_STANDARD = 50;
function calculateShipping(weight) {
if (weight > HEAVY_WEIGHT_THRESHOLD) {
return SHIPPING_HEAVY;
}
return SHIPPING_STANDARD;
}
```
### 技巧 4改善命名
**Before**
```javascript
function calc(x) {
return x * 0.9;
}
```
**After**
```javascript
function applyDiscount(price) {
const DISCOUNT_RATE = 0.9;
return price * DISCOUNT_RATE;
}
```
### 技巧 5簡化條件
**Before**
```javascript
function canPurchase(user, product) {
if (user.age >= 18) {
if (user.balance >= product.price) {
if (product.stock > 0) {
return true;
}
}
}
return false;
}
```
**After**
```javascript
function canPurchase(user, product) {
return user.age >= 18
&& user.balance >= product.price
&& product.stock > 0;
}
```
## 何時停止重構?
Kent Beck 的判斷標準:
### 停止的信號
**可以停止了**
- 沒有明顯的重複
- 程式碼清楚易懂
- 命名恰當
- 函式簡短
**也可以停止**
- 想不到明顯的改善
- 繼續重構的收益不大
- 想寫下一個測試了
### 不要過度重構
```javascript
// ✅ 夠好了
function calculateTotal(items) {
let total = 0;
for (const item of items) {
total += item.price * item.quantity;
}
return total;
}
// ❌ 過度了(除非真的需要)
class TotalCalculator {
constructor(strategy) {
this.strategy = strategy;
}
calculate(items) {
return this.strategy.compute(items);
}
}
class StandardCalculationStrategy {
compute(items) {
return items.reduce((sum, item) =>
sum + this.calculateItemTotal(item), 0);
}
calculateItemTotal(item) {
return new Money(item.price)
.multiply(item.quantity)
.getAmount();
}
}
// ... 太複雜了!
```
## 重構的節奏
Kent Beck 建議的節奏:
```
快速寫測試(幾分鐘)
快速讓測試通過(幾分鐘)
快速重構(幾分鐘)← 現在在這裡
回到寫測試
```
**每個階段都要快**
- 不要花太久在重構
- 幾分鐘就好
- 保持節奏
## 更新 journey.md
```markdown
#### 🔵 Refactor - 重構
重構內容:
1. {改善項目 1}
- 重構前:{簡述}
- 重構後:{簡述}
- 原因:{為什麼}
2. {改善項目 2}
...
測試狀態:✅ 持續通過
#### 📝 下一步
執行 /kb-review 回顧下一步
```
## 範例Money 的重構
### 重構前
```javascript
class Dollar {
constructor(amount) {
this.amount = amount;
}
times(multiplier) {
return new Dollar(this.amount * multiplier);
}
}
class Franc {
constructor(amount) {
this.amount = amount;
}
times(multiplier) {
return new Franc(this.amount * multiplier);
}
}
```
**發現重複**Dollar 和 Franc 幾乎一樣!
### 重構步驟
**Step 1提取父類別**
```javascript
class Money {
constructor(amount) {
this.amount = amount;
}
}
class Dollar extends Money {
times(multiplier) {
return new Dollar(this.amount * multiplier);
}
}
class Franc extends Money {
times(multiplier) {
return new Franc(this.amount * multiplier);
}
}
```
測試:✅ 通過
**Step 2將 times 提升到父類別**
```javascript
class Money {
constructor(amount) {
this.amount = amount;
}
times(multiplier) {
return new Money(this.amount * multiplier);
}
}
class Dollar extends Money {}
class Franc extends Money {}
```
測試:✅ 通過
**消除了重複!**
## 重構的勇氣來自哪裡?
Kent Beck**測試給你重構的勇氣**
```
沒有測試:
😰 不敢改程式碼
😰 怕改壞東西
😰 程式碼越來越爛
有測試:
😎 隨時可以重構
😎 測試會告訴你有沒有改壞
😎 程式碼越來越好
```
## 下一步
重構完成?執行:
```
/kb-review
```
回顧這一輪,計劃下一步!
## 記住 Kent Beck 的話
> "重構的時機是在測試通過之後。"
> "重複是設計的敵人。"
> "小步重構,頻繁測試,永遠保持綠燈。"
> "測試讓你有勇氣重構,重構讓程式碼保持清潔。"

359
commands/kb-review.md Normal file
View File

@@ -0,0 +1,359 @@
---
description: 回顧這一輪 TDD決定下一步。遵循 Kent Beck 的持續演進理念。
---
# TDD Review - 回顧與規劃下一步
完成一輪 Red-Green-Refactor 後,停下來思考。
**【功能名】**{{feature_name}}
## Kent Beck 的回顧理念
> "每完成一個測試,都要問自己:我學到了什麼?"
> "下一個測試不是計劃出來的,而是從經驗中浮現的。"
## 回顧步驟
### 1. 回顧這一輪
在 journey.md 中更新:
```markdown
### 第 N 輪完成 - {日期時間}
#### 💭 回顧與學習
**這一輪做了什麼**
{簡述這輪的測試和實作}
**學到了什麼**
- {發現 1}
- {發現 2}
- {驚喜或意外}
**設計如何演進**
{設計有什麼改變?}
**目前的進度感覺**
{覺得進度如何?順利還是卡住?}
```
### 2. 回顧問題清單
Kent Beck 建議問自己:
#### 關於測試
- ✅ 這個測試真的需要嗎?
- ✅ 測試太大了嗎?
- ✅ 測試太小了嗎?
- ✅ 測試名稱清楚嗎?
#### 關於實作
- ✅ 實作還是假的嗎?
- ✅ 需要三角測量嗎?
- ✅ 有沒有過度設計?
- ✅ 程式碼清楚嗎?
#### 關於重構
- ✅ 還有重複嗎?
- ✅ 命名恰當嗎?
- ✅ 結構清晰嗎?
- ✅ 需要更多重構嗎?
#### 關於節奏
- ✅ 步伐太大還是太小?
- ✅ 速度合適嗎?
- ✅ 感覺順暢嗎?
### 3. 決定下一步
Kent Beck有三個選擇
#### 選項 1繼續測試最常見
**何時選擇**
- 還有明顯的功能未實作
- 想到下一個測試案例
- 設計還在演進中
**下一步**
```
/kb-red
```
**在 journey.md 記錄**
```markdown
#### 🎯 下一步計劃
**下一個測試想法**
{為什麼要寫這個測試?}
**預期會發生什麼**
{這個測試會逼出什麼實作?}
執行 /kb-red
```
#### 選項 2暫停休息
**何時選擇**
- 累了
- 卡住了
- 需要思考
**暫停建議**
- 寫下目前的想法
- 記錄卡住的地方
- 休息一下再回來
**在 journey.md 記錄**
```markdown
#### ⏸️ 暫停點
**目前狀態**
{進行到哪裡了}
**下次回來要做什麼**
{下一步的提示}
**卡住的問題**(如果有)
{什麼地方不確定}
```
#### 選項 3結束這個會話
**何時選擇**
- 目前的目標達成了
- 找到自然的停止點
- 功能暫時夠用了
**在 journey.md 記錄**
```markdown
#### 🎉 會話結束
**達成的成果**
{列出完成的功能}
**目前的狀態**
- 測試數量:{數量}
- 測試通過率100%
- 程式碼品質:{評估}
**未來可能的方向**
- {可能的擴展 1}
- {可能的擴展 2}
```
## 心理 To-Do List
Kent Beck 建議維護一個簡單的 To-Do List
### 在 journey.md 中
```markdown
## 心理 To-Do List
### 已完成
- [x] 基本乘法運算
- [x] 處理負數
### 下一步可能的測試
- [ ] 加法運算
- [ ] 相等性比較
- [ ] 不同幣別
### 設計想法
- [ ] 考慮提取 Money 父類別
- [ ] 可能需要 Currency 概念
```
**特點**
- 非正式的
- 隨時可以改
- 不是承諾
- 只是提醒
## 調整步伐
### 如果進度太慢
Kent Beck**加大步伐**
- 寫更大的測試
- 用 Obvious Implementation
- 跳過明顯的步驟
### 如果感到不安
Kent Beck**縮小步伐**
- 寫更小的測試
- 用 Fake It
- 三角測量
- 更頻繁測試
## 範例反思記錄
### 順利的情況
```markdown
### 第 3 輪完成 - 2025-10-15 22:00
#### 💭 回顧與學習
**這一輪做了什麼**
測試了負數乘法,確認 Dollar(-5).times(2) 得到 -10。
**學到了什麼**
- 現有實作自動支援負數
- 不需要特殊處理
- 乘法的數學性質自然延伸
**設計如何演進**
沒有改變,驗證了設計的穩健性。
**目前的進度感覺**
很順利!基本的乘法功能已經穩定。
#### 🎯 下一步計劃
**下一個測試想法**
測試加法Dollar(5).plus(Dollar(3)) 應該等於 Dollar(8)
**預期會發生什麼**
需要新增 plus 方法,可能會很直接。
執行 /kb-red
```
### 卡住的情況
```markdown
### 第 5 輪完成 - 2025-10-15 22:30
#### 💭 回顧與學習
**這一輪做了什麼**
嘗試實作不同幣別的比較。
**學到了什麼**
- 設計變複雜了
- 不確定該怎麼處理幣別
- 可能需要重新思考
**設計如何演進**
引入了 Currency 概念,但感覺有點勉強。
**目前的進度感覺**
有點卡住。可能步伐太大了。
#### ⏸️ 暫停點
**目前狀態**
Dollar 和 Franc 可以獨立運作,但比較功能還不清楚。
**下次回來要做什麼**
考慮回退到更簡單的設計,或者寫更小的測試。
**卡住的問題**
- 幣別應該是字串還是物件?
- 需要匯率轉換嗎?
- 設計是否過度複雜?
可能需要回到更小的步伐,寫一個更簡單的測試。
```
## 長期視角
### Kent Beck 的"完成"定義
TDD **沒有**真正的完成:
- 軟體是演進的
- 永遠可以加新測試
- 永遠可以改善設計
- 永遠可以學到新東西
### 知道何時停止
**可以暫時停止的信號**
- 目前的需求滿足了
- 設計達到一個穩定點
- 沒有明顯的下一步
- 想要轉向其他功能
**不是因為**
- "測試寫夠了"
- "覆蓋率達標了"
- "文件說的都做完了"
## 節奏意識
Kent Beck 強調 TDD 的節奏:
```
🔴 Red → 緊張(寫測試)
🟢 Green → 放鬆(通過了!)
🔵 Refactor → 改善(重構)
💭 Review → 回顧(現在)
```
**好的節奏**
- 快速循環(幾分鐘)
- 頻繁的綠燈
- 定期的反思
**不好的節奏**
- 卡在紅燈太久
- 測試寫太大
- 實作太複雜
- 忘記重構
- 沒有回顧
## 下一步選擇
### 繼續前進
```bash
/kb-red
```
### 查看旅程記錄
```bash
cat docs/tdd/{feature_name}/journey.md
```
### 執行所有測試
```bash
npm test
```
## 記住 Kent Beck 的話
> "TDD 不是一個目的地,而是一個旅程。"
> "每個測試都教會你一些東西。"
> "下一步不是計劃出來的,而是從經驗中浮現的。"
> "當你不知道下一步時,寫一個你知道答案的測試。"
> "信任這個過程,設計會自然演進。"
---
## 完整循環
你已經完成一輪完整的 TDD 循環!
```
/kb-start ← 從這裡開始
/kb-red ← 寫測試 (Red)
/kb-green ← 實作 (Green)
/kb-refactor ← 重構 (Refactor)
/kb-review ← 回顧(你在這裡)
循環回 /kb-red 或結束
```
準備好下一輪了嗎? 🚀

129
commands/kb-start.md Normal file
View File

@@ -0,0 +1,129 @@
---
description: 啟動新的 TDD 會話。遵循 Kent Beck 的純粹 TDD 理念,從第一個測試開始。
---
# TDD Start - 啟動 TDD 會話
開始一個新的 TDD 開發會話。遵循 Kent Beck 的理念:從測試開始,讓設計演進。
**【功能想法】**{{brief_idea}}
## Kent Beck 的起點
> "開始時不要想太多,從最簡單的測試開始。"
**不要做:**
- ❌ 寫完整的需求文件
- ❌ 規劃所有測試案例
- ❌ 事先設計類別結構
- ❌ 想太多細節
**要做:**
- ✅ 想一個最簡單的情境
- ✅ 寫出第一個測試
- ✅ 讓測試驅動設計
## 開始步驟
### 1. 建立開發記錄
`docs/tdd/{feature_name}/` 建立:
```
journey.md - TDD 開發旅程記錄(不是計劃)
```
### 2. 想第一個測試
問自己:
- 什麼是最簡單的使用情境?
- 什麼是最明顯的行為?
- 從哪裡開始最容易?
**範例思考過程:**
```
功能想法:「計算購物車總價」
❌ 不要想:
- 需要支援優惠券
- 需要處理多幣別
- 需要計算稅金
- ...
✅ 只想:
「一個商品的購物車,總價應該等於商品價格」
這就是第一個測試!
```
### 3. journey.md 初始格式
```markdown
# {feature_name} TDD Journey
## 開始想法
{簡單描述要做什麼1-2 句話}
## TDD 循環記錄
### 第 1 輪 - {日期時間}
#### 🤔 想法
{為什麼要寫這個測試?}
#### 📝 下一步
執行 /kb-red 寫第一個測試
```
## 心態準備
Kent Beck 強調的 TDD 心態:
**好奇心**
- "如果我這樣寫測試會怎樣?"
- 用測試來探索問題
**勇氣**
- 敢於寫會失敗的測試
- 敢於用最簡單的方式實作
**簡單**
- 從最簡單的開始
- 不要過度設計
**反饋**
- 頻繁執行測試
- 快速得到反饋
## 範例:開始一個 Money 類別
```markdown
# Money TDD Journey
## 開始想法
建立一個 Money 類別,可以處理金錢運算。
## TDD 循環記錄
### 第 1 輪 - 2025-10-15 21:30
#### 🤔 想法
最簡單的情境5 元乘以 2 應該等於 10 元。
#### 📝 下一步
執行 /kb-red 寫第一個測試
```
## 下一步
準備好了嗎?執行:
```
/kb-red
```
開始寫你的第一個測試!
## 記住 Kent Beck 的話
> "TDD 的目的不是測試,而是思考。"
> "從最簡單的測試開始,讓複雜度自然演進。"
> "如果不知道該寫什麼測試,那就寫一個你知道答案的測試。"

61
plugin.lock.json Normal file
View File

@@ -0,0 +1,61 @@
{
"$schema": "internal://schemas/plugin.lock.v1.json",
"pluginId": "gh:cashwu/claude-code-tdd-marketplace:plugins/kent-beck-tdd",
"normalized": {
"repo": null,
"ref": "refs/tags/v20251128.0",
"commit": "d87d435feabff2801b6588c5c30da50c827463e5",
"treeHash": "7465967f0db91be17641bb12a329d7b3c5de8d78a821220c2871175c1466945c",
"generatedAt": "2025-11-28T10:14:30.209144Z",
"toolVersion": "publish_plugins.py@0.2.0"
},
"origin": {
"remote": "git@github.com:zhongweili/42plugin-data.git",
"branch": "master",
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
},
"manifest": {
"name": "kent-beck-tdd",
"description": "Kent Beck's organic TDD: Let design emerge naturally through small, iterative test cycles",
"version": "1.0.0"
},
"content": {
"files": [
{
"path": "README.md",
"sha256": "b2a7d3905094705b6e1c8a4f4522caa81c6fde713a443bc1e70e1cf6aff859fd"
},
{
"path": ".claude-plugin/plugin.json",
"sha256": "846c3bcb35ac3800de4df92e658001e7aa4f7c542f26ec83eb5e0ed254367b4b"
},
{
"path": "commands/kb-review.md",
"sha256": "939f2259438c215ffb981075309ee216b850fc4c2324afb87fbbe29bc18c18be"
},
{
"path": "commands/kb-red.md",
"sha256": "64ad38a10362a984b6c71ea433a99a782e07f95889b4bebc30655585b85d5988"
},
{
"path": "commands/kb-start.md",
"sha256": "8f277395e542800a7c5ae4abceefc3e3474e64c592fda5b564b485eb467692cf"
},
{
"path": "commands/kb-refactor.md",
"sha256": "d952ee0fb05993f76e8d8bd192e8edb41e9587778b7b6e306c45e82edd1d683d"
},
{
"path": "commands/kb-green.md",
"sha256": "4f6f72649d03f65b97dfa7db1b264106f7fb6bd13187430c18d0792cfd85f4a1"
}
],
"dirSha256": "7465967f0db91be17641bb12a329d7b3c5de8d78a821220c2871175c1466945c"
},
"security": {
"scannedAt": null,
"scannerVersion": null,
"flags": []
}
}