aile-tdd

SKILL.md

測試驅動開發 (TDD)

来源原 Skill

  • 来源:superpowers TDD 能力(已迁移为 aile-only)
  • 策略:保留 RED → GREEN → REFACTOR 闭环,并与团队阶段流程对齐。

概述

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

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

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

何時使用

總是:

  • 新功能
  • 錯誤修復
  • 重構
  • 行為改變

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

  • 一次性原型
  • 生成的代碼
  • 配置文件

想「這次跳過 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

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

Weekly Installs
11
First Seen
Feb 14, 2026
Installed on
opencode11
github-copilot11
codex11
kimi-cli11
gemini-cli11
amp11