test-driven-development

先寫測試。看著它失敗。編寫最少的程式碼即可通過。

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "test-driven-development" with this command: npx skills add zhucl1006/ailesuperpowers/zhucl1006-ailesuperpowers-test-driven-development

測試驅動開發 (TDD)

概述

先寫測試。看著它失敗。編寫最少的程式碼即可通過。

核心原則: 如果你沒有看到測試失敗,你就不知道它是否測試了正確的東西。

**違反規則的字面意思就是違反規則的精神。 **

何時使用

總是:

  • 新功能

  • 錯誤修復

  • 重構

  • 行為改變

例外(詢問你的人類夥伴):

  • 一次性原型

  • 生成的代碼

  • 配置文件

想「這次跳過 TDD」嗎?停止吧。這就是合理化。

鐵律

NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST

測試前先寫代碼?刪除它。重新開始。

沒有例外:

  • 不要將其保留為“參考”

  • 編寫測試時不要“適應”它

  • 別看它

  • 刪除就是刪除

實施新的測試。時期。

紅綠重構

digraph tdd_cycle { rankdir=LR; red [label="RED\nWrite failing test", shape=box, style=filled, fillcolor="#ffcccc"]; verify_red [label="Verify fails\ncorrectly", shape=diamond]; green [label="GREEN\nMinimal code", shape=box, style=filled, fillcolor="#ccffcc"]; verify_green [label="Verify passes\nAll green", shape=diamond]; refactor [label="REFACTOR\nClean up", shape=box, style=filled, fillcolor="#ccccff"]; next [label="Next", shape=ellipse];

red -> verify_red;
verify_red -> green [label="yes"];
verify_red -> red [label="wrong\nfailure"];
green -> verify_green;
verify_green -> refactor [label="yes"];
verify_green -> green [label="no"];
refactor -> verify_green [label="stay\ngreen"];
verify_green -> next;
next -> red;

}

紅色 - 寫入失敗測試

編寫一個最小的測試來顯示應該發生什麼。

<好>

test('retries failed operations 3 times', async () => { let attempts = 0; const operation = () => { attempts++; if (attempts < 3) throw new Error('fail'); return 'success'; };

const result = await retryOperation(operation);

expect(result).toBe('success'); expect(attempts).toBe(3); });

清晰的名字,測試真實的行為,一件事 </好>

<壞>

test('retry works', async () => { const mock = jest.fn() .mockRejectedValueOnce(new Error()) .mockRejectedValueOnce(new Error()) .mockResolvedValueOnce('success'); await retryOperation(mock); expect(mock).toHaveBeenCalledTimes(3); });

模糊的名稱,測試模擬而不是代碼 </壞>

要求:

  • 一種行為

  • 清晰的名字

  • 真實程式碼(除非不可避免,否則不進行模擬)

驗證紅色 - 觀察失敗

**強制的。切勿跳過。 **

npm test path/to/test.test.ts

確認:

  • 測試失敗(不是錯誤)

  • 預計會出現失敗訊息

  • 由於功能缺失(不是拼寫錯誤)而失敗

**測試通過? ** 您正在測試現有行為。修復測試。

**測試錯誤? ** 修復錯誤,重新運行,直到正確失敗。

綠色 - 最少代碼

編寫最簡單的程式碼來通過測試。

<好>

async function retryOperation<T>(fn: () => Promise<T>): Promise<T> { for (let i = 0; i < 3; i++) { try { return await fn(); } catch (e) { if (i === 2) throw e; } } throw new Error('unreachable'); }

只要夠通過即可 </好>

<壞>

async function retryOperation<T>( fn: () => Promise<T>, options?: { maxRetries?: number; backoff?: 'linear' | 'exponential'; onRetry?: (attempt: number) => void; } ): Promise<T> { // YAGNI }

過度設計 </壞>

不要添加功能、重構其他程式碼或在測試之外進行「改進」。

驗證綠色 - 觀察它通過

**強制的。 **

npm test path/to/test.test.ts

確認:

  • 測試通過

  • 其他測試仍然通過

  • 輸出原始(沒有錯誤、警告)

**測試失敗? ** 修復代碼,而不是測試。

**其他測試失敗? ** 立即修復。

重構 - 清理

僅綠色之後:

  • 刪除重複項

  • 改進名字

  • 提取助手

保持測試綠色。不要添加行為。

重複

下一個功能的下一個失敗測試。

良好的測試

品質 好 不好

最小 一件事。名字中的“和”?分開它。 test('validates email and domain and whitespace')

清除 名稱描述行為 test('test1')

表明意圖 所需需要的API 模糊了程序代碼執行

為什麼訂單很重要

“我會在之後編寫測試來驗證它是否有效”

程式碼通過後編寫的測試立即通過。立即通過並不能證明什麼:

  • 可能測試錯誤的東西

  • 可能測試實施,而不是行為

  • 可能會錯過您忘記的邊緣情況

  • 你從未遇到過它捕獲 bug

測試優先迫使您看到測試失敗,證明它確實測試了某些東西。

「我已經手動測試了所有邊緣情況」

手動測試是臨時的。您認為您測試了所有內容,但:

  • 沒有記錄您測試的內容

  • 代碼更改後無法重新運行

  • 在壓力下容易忘記案件

  • “我嘗試了一下就成功了”≠全面

自動化測試是系統化的。他們每次都以同樣的方式奔跑。

「消除X小時的工作就是浪費」

沉沒成本謬誤。時間已經過去了。您現在的選擇:

  • 使用TDD刪除並重寫(多花幾個小時,高可信度)

  • 保留它並在之後添加測試(30 分鐘,低置信度,可能存在錯誤)

“浪費”是保留你不信任的代碼。沒有真正測試的工作代碼是技術債。

「TDD 是教條主義的,務實意味著適應」

TDD 很務實:

  • 在提交之前發現錯誤(比之後調試更快)

  • 防止回歸(測試立即捕獲中斷)

  • 文檔行為(測試顯示如何使用程式碼)

  • 啟用重構(自由更改,測試捕獲中斷)

“實用”快捷方式=生產中的調試=速度較慢。

「達到相同目標後進行測試 - 這是精神而不是儀式」

不。回答後測試“這有什麼作用?”測試優先回答“這應該做什麼?”

之後的測試因您的實現而存在偏差。您測試您構建的內容,而不是測試所需的內容。您驗證記住的邊緣情況,而不是發現的情況。

測試優先強制在實施之前發現邊緣情況。測試 - 驗證您記住了所有內容(您沒有記住)。

≠ TDD 經過 30 分鐘的測試。你得到了保險,失去了證明測試的工作。

常見的合理化理由

對不起 現實

“太簡單了,無法測試” 簡單的代碼中斷。測試需要 30 秒。

“之後我會測試” 測試立即通過並不能證明什麼。

“達到相同目標後再進行測試” Tests-after =“這是做什麼的?”測試優先=“這應該做什麼?”

“已經手動測試” 臨時性≠系統性。沒有記錄,無法重新運行。

“刪除X小時是浪費” 沉沒成本謬誤。保留默認驗證的代碼是技術債。

「留作參考,先寫測驗」 你會適應它。後面就是測試了刪除就是刪除的意思。

“需要先探索” 美好的。放棄探索,從TDD開始。

“努力測試=設計不清楚” 聽聽測試。難以測試=難以使用。

“TDD 會讓我放慢速度” TDD 比調試更快。務實=測試第一。

“手動測試速度更快” 手冊不能證明邊緣情況。您將重新測試每個變更。

“現有代碼沒有經過測試” 你正在改進它。為現有代碼添加測試。

危險信號 - 停止並重新開始

  • 測試前的代碼

  • 實施後測試

  • 測試立即通過

  • 無法解釋測試失敗的原因

  • “稍後”添加測試

  • 合理化“就這一次”

  • “我已經手動測試過了”

  • “達到相同目的後進行測試”

  • “這是關於精神而不是儀式”

  • “保留作為參考”或“改編現有代碼”

  • “已經花了X個小時了,刪掉太浪費了”

  • “TDD很教條,我很務實”

  • “這是不同的,因為……”

**所有這些意味著:刪除計劃碼。從TDD開始。 **

示例:錯誤修復

錯誤: 接受空電子郵件

紅色的

test('rejects empty email', async () => { const result = await submitForm({ email: '' }); expect(result.error).toBe('Email required'); });

驗證紅色

$ npm test FAIL: expected 'Email required', got undefined

綠色的

function submitForm(data: FormData) { if (!data.email?.trim()) { return { error: 'Email required' }; } // ... }

驗證綠色

$ npm test PASS

重構 如果需要,提取多個欄位的驗證。

驗證清單

在標記工作完成之前:

  • 每個新函數/方法都有一個測試

  • 在實施之前觀察每個測試的失敗

  • 每個測驗都因預期原因而失敗(功能缺失,而非拼字錯誤)

  • 編寫最少的代碼來通過每個測試

  • 所有測試均通過

  • 輸出原始(沒有錯誤、警告)

  • 測試使用真實程式碼(僅在不可避免時才進行模擬)

  • 涵蓋的邊緣情況和錯誤

無法完成所有中斷嗎?你跳過了 TDD。重新開始。

卡住時

問題 解決方案

不知道如何測試 編寫想要的API。先寫斷言。詢問你的人類夥伴。

測試太複雜 設計太複雜了。簡化界面。

必須嘲笑一切 代碼耦合性太強。使用依賴注入。

測試設定巨大 提取助手。還是很複雜?簡化設計。

偵錯集成

發現錯誤了嗎?寫一個失敗的測試來重置它。遵循TDD週期。測試證明可以修復並阻止回歸。

未經測試切勿修復錯誤。

測試反模式

在新增模擬或測試實用程式時,請閱讀@testing-anti-patterns.md分區常見陷阱:

  • 測試模擬行為而不是真實行為

  • 將僅測試方法添加到生產類中

  • 在不瞭解依賴關係的情況下進行模擬

最終規則

Production code → test exists and failed first Otherwise → not TDD

未經您的人類伴侶許可,也不例外。

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

aile-subagent-dev

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

aile-code-review

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

requesting-code-review

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

finishing-a-development-branch

No summary provided by upstream source.

Repository SourceNeeds Review