# 延遲計數系統測試規格 **目的**: 將您的延遲計數需求轉換為可執行的測試案例 **測試範圍**: 跳過功能、排序邏輯、答錯處理、優先級管理 **測試方式**: Jest + React Testing Library **最後更新**: 2025-10-03 --- ## 🧪 **核心需求測試** ### **測試1: 跳過功能基礎邏輯** ```typescript describe('延遲計數系統 - 跳過功能', () => { test('當使用者在答題時點擊跳過,該題目會跳過', () => { // 準備測試數據 const initialCards = [ { id: '1', word: 'evidence', skipCount: 0, wrongCount: 0, isCompleted: false } ] const currentIndex = 0 // 執行跳過操作 const result = handleSkip(initialCards, currentIndex) // 驗證結果 expect(result.updatedCards[0].skipCount).toBe(1) expect(result.updatedCards[0].wrongCount).toBe(0) // 不影響答錯次數 expect(result.nextIndex).toBe(1) // 移動到下一題 expect(result.updatedCards[0].isCompleted).toBe(false) // 未完成狀態 }) test('同一題目可以多次跳過,次數累加', () => { const cards = [ { id: '1', word: 'test', skipCount: 2, wrongCount: 1, isCompleted: false } ] const result = handleSkip(cards, 0) expect(result.updatedCards[0].skipCount).toBe(3) // 從2增加到3 expect(result.updatedCards[0].wrongCount).toBe(1) // 保持不變 }) }) ``` ### **測試2: 排序優先級邏輯** ```typescript describe('延遲計數系統 - 排序邏輯', () => { test('題目排序應該是被跳過次數越少越前面', () => { // 準備不同延遲分數的卡片 const cards = [ { id: '1', word: 'card1', skipCount: 3, wrongCount: 1, originalOrder: 1, isCompleted: false }, // 延遲分數: 4 { id: '2', word: 'card2', skipCount: 1, wrongCount: 0, originalOrder: 2, isCompleted: false }, // 延遲分數: 1 { id: '3', word: 'card3', skipCount: 0, wrongCount: 0, originalOrder: 3, isCompleted: false }, // 延遲分數: 0 { id: '4', word: 'card4', skipCount: 2, wrongCount: 0, originalOrder: 4, isCompleted: false } // 延遲分數: 2 ] // 執行排序 const sorted = sortCardsByPriority(cards) // 驗證排序結果 (延遲分數由小到大) expect(sorted[0].id).toBe('3') // 延遲分數 0 expect(sorted[1].id).toBe('2') // 延遲分數 1 expect(sorted[2].id).toBe('4') // 延遲分數 2 expect(sorted[3].id).toBe('1') // 延遲分數 4 }) test('延遲分數相同時按原始順序排列', () => { const cards = [ { id: '1', skipCount: 1, wrongCount: 0, originalOrder: 3, isCompleted: false }, // 延遲分數: 1 { id: '2', skipCount: 0, wrongCount: 1, originalOrder: 1, isCompleted: false }, // 延遲分數: 1 { id: '3', skipCount: 1, wrongCount: 0, originalOrder: 2, isCompleted: false } // 延遲分數: 1 ] const sorted = sortCardsByPriority(cards) // 相同延遲分數按originalOrder排序 expect(sorted[0].originalOrder).toBe(1) expect(sorted[1].originalOrder).toBe(2) expect(sorted[2].originalOrder).toBe(3) }) }) ``` ### **測試3: 答錯效果等同跳過** ```typescript describe('延遲計數系統 - 答錯處理', () => { test('答錯的效果和跳過一樣都會被註記一次', () => { const initialCard = { id: '1', word: 'test', skipCount: 0, wrongCount: 0, isCompleted: false } // 測試跳過效果 const skippedResult = handleSkip([initialCard], 0) const skippedScore = skippedResult.updatedCards[0].skipCount + skippedResult.updatedCards[0].wrongCount // 測試答錯效果 const wrongResult = handleWrongAnswer([initialCard], 0) const wrongScore = wrongResult.updatedCards[0].skipCount + wrongResult.updatedCards[0].wrongCount // 兩者都應該增加1分延遲分數 expect(skippedScore).toBe(1) expect(wrongScore).toBe(1) }) test('信心度1-2算答錯,3算答對', () => { const card = { id: '1', word: 'test', skipCount: 0, wrongCount: 0, isCompleted: false } // 信心度1 (模糊) → 答錯 const result1 = handleConfidenceAnswer([card], 0, 1) expect(result1.updatedCards[0].wrongCount).toBe(1) expect(result1.updatedCards[0].isCompleted).toBe(false) // 信心度2 (一般) → 答對 const result2 = handleConfidenceAnswer([card], 0, 2) expect(result2.updatedCards[0].isCompleted).toBe(true) expect(result2.updatedCards[0].wrongCount).toBe(0) // 不增加 // 信心度3 (熟悉) → 答對 const result3 = handleConfidenceAnswer([card], 0, 3) expect(result3.updatedCards[0].isCompleted).toBe(true) }) }) ``` ### **測試4: 排序不排除邏輯** ```typescript describe('延遲計數系統 - 不排除原則', () => { test('被跳過只是排序問題,不是直接排除', () => { const cards = [ { id: '1', word: 'high-delay', skipCount: 10, wrongCount: 5, isCompleted: false }, // 高延遲 { id: '2', word: 'normal', skipCount: 0, wrongCount: 0, isCompleted: false }, // 正常 { id: '3', word: 'completed', skipCount: 2, wrongCount: 1, isCompleted: true } // 已完成 ] const sorted = sortCardsByPriority(cards) // 驗證沒有卡片被排除 expect(sorted).toHaveLength(3) expect(sorted.find(c => c.id === '1')).toBeDefined() // 高延遲卡片仍存在 // 驗證排序正確 expect(sorted[0].id).toBe('2') // 正常卡片優先 expect(sorted[1].id).toBe('1') // 延遲卡片其次 expect(sorted[2].id).toBe('3') // 已完成卡片最後 }) test('即使跳過很多次的卡片仍會被練習', () => { const cards = [ { id: '1', word: 'difficult', skipCount: 20, wrongCount: 15, isCompleted: false } ] const sorted = sortCardsByPriority(cards) expect(sorted).toHaveLength(1) expect(sorted[0].id).toBe('1') // 即使延遲分數很高,仍然存在 }) }) ``` ### **測試5: 完整流程集成** ```typescript describe('延遲計數系統 - 完整流程', () => { test('完整的學習會話流程', () => { // 初始狀態: 3張卡片 let cards = [ { id: '1', word: 'easy', skipCount: 0, wrongCount: 0, originalOrder: 1, isCompleted: false }, { id: '2', word: 'medium', skipCount: 0, wrongCount: 0, originalOrder: 2, isCompleted: false }, { id: '3', word: 'hard', skipCount: 0, wrongCount: 0, originalOrder: 3, isCompleted: false } ] // 第1輪: easy答對, medium跳過, hard答錯 cards = handleConfidenceAnswer(cards, 0, 3).updatedCards // easy完成 cards = handleSkip(cards, 1).updatedCards // medium跳過+1 cards = handleWrongAnswer(cards, 2).updatedCards // hard答錯+1 // 排序後應該是: medium, hard (延遲分數都是1,按原順序) const sorted1 = sortCardsByPriority(cards.filter(c => !c.isCompleted)) expect(sorted1[0].word).toBe('medium') expect(sorted1[1].word).toBe('hard') // 第2輪: medium再次跳過, hard答對完成 cards = handleSkip(cards, 1).updatedCards // medium跳過+1 (總共2次) cards = handleConfidenceAnswer(cards, 2, 2).updatedCards // hard完成 // 最終只剩medium未完成,延遲分數為2 const remaining = cards.filter(c => !c.isCompleted) expect(remaining).toHaveLength(1) expect(remaining[0].word).toBe('medium') expect(remaining[0].skipCount).toBe(2) }) }) ``` --- ## 🎯 **測試實作函數** ### **需要實作的核心函數** ```typescript // 這些是測試中使用的函數,需要在實際代碼中實作 interface TestResult { updatedCards: CardState[] nextIndex: number } // 跳過處理函數 function handleSkip(cards: CardState[], currentIndex: number): TestResult // 答錯處理函數 function handleWrongAnswer(cards: CardState[], currentIndex: number): TestResult // 信心度答題函數 function handleConfidenceAnswer(cards: CardState[], currentIndex: number, confidence: 1|2|3): TestResult // 排序函數 function sortCardsByPriority(cards: CardState[]): CardState[] ``` --- ## 📊 **測試覆蓋範圍** ### **功能覆蓋** - ✅ 跳過次數計數 - ✅ 答錯次數計數 - ✅ 優先級排序邏輯 - ✅ 完成狀態管理 - ✅ 多次操作累計效果 ### **邊界條件測試** - ✅ 極高延遲分數的卡片 - ✅ 相同延遲分數的排序 - ✅ 全部完成的情況 - ✅ 單卡片情況 ### **集成測試** - ✅ 完整學習會話流程 - ✅ 多輪練習的狀態變化 - ✅ 不同信心度的處理 --- ## 🚀 **TDD開發流程** ### **第1步: Red (測試失敗)** ```bash npm test delay-counting-system.test.ts # 所有測試應該失敗,因為函數還沒實作 ``` ### **第2步: Green (最小實作)** ```typescript // 實作最簡單的版本讓測試通過 const handleSkip = (cards, index) => { const newCards = [...cards] newCards[index] = { ...newCards[index], skipCount: newCards[index].skipCount + 1 } return { updatedCards: newCards, nextIndex: index + 1 } } ``` ### **第3步: Refactor (重構優化)** ```typescript // 重構為更優雅的版本,保持測試通過 const handleSkip = useCallback((cards: CardState[], currentIndex: number) => { const updatedCards = cards.map((card, index) => index === currentIndex ? { ...card, skipCount: card.skipCount + 1 } : card ) return { updatedCards, nextIndex: getNextIndex(updatedCards, currentIndex) } }, []) ``` --- ## 🎯 **測試的業務價值** ### **確保需求實現** - ✅ 您的4個核心需求都有對應測試 - ✅ 邊界情況都有覆蓋 - ✅ 集成場景完整測試 ### **回歸保護** - ✅ 未來修改不會破壞核心邏輯 - ✅ 重構時有安全保障 - ✅ 新功能不會影響現有行為 ### **需求文檔化** - ✅ 測試即是可執行的需求規格 - ✅ 開發者可以直接理解期望行為 - ✅ 產品經理可以驗證實作正確性 --- ## 📋 **測試執行清單** ### **開發前** - [ ] 所有測試都失敗 (因為還沒實作) - [ ] 測試準確描述了您的需求 ### **開發中** - [ ] 逐個實作函數讓測試通過 - [ ] 保持測試綠燈狀態 - [ ] 重構時確保測試不破壞 ### **完成後** - [ ] 所有測試通過 - [ ] 覆蓋率達到90%+ - [ ] 邊界條件都正確處理 --- **這些測試完美地捕捉了您的延遲計數需求,可以指導TDD開發,確保實作完全符合您的預期!** 🎯 *測試規格制定: 2025-10-03* *基於用戶明確需求* *TDD開發就緒*