tdd 是一項 Test Driven Development skill,重點在於教你落實嚴格的 red-green-refactor 流程、透過公開介面撰寫以行為為核心的測試、採用偏 integration-style 的測試方式,並只在系統邊界使用 mocking。
這項 skill 的評分為 78/100,代表它是相當穩健的目錄收錄候選:使用者大致可以期待代理判斷何時該啟用它,並獲得比通用提示更聚焦於 TDD 的指引;但不應期待它已經提供完整腳本化的端到端工作流程。
- frontmatter 的觸發訊號很明確:清楚點出 TDD、red-green-refactor、integration tests,以及 test-first 的使用情境。
- 對「該測什麼」與「該避免什麼」提供了不錯的實務指引,並在 `tests.md` 與 `mocking.md` 中給出具體範例。
- 也補充了有助於落實 TDD 的重要概念,包括介面設計、deep modules、mocking boundaries,以及每輪循環後的重構提示。
- `SKILL.md` 中沒有提供快速上手或安裝/執行指引,因此代理需要自行推斷如何在當前工作階段套用這項 skill。
- 內容以原則與範例說明為主;缺少針對真實功能開發或 bug 修復的逐步 TDD 實作流程。
tdd skill 概覽
tdd skill 是一份專注於測試優先開發的指南,核心在於嚴格執行 red-green-refactor 迴圈,不是那種泛泛地叫模型「順手寫幾個測試」的提示。它特別適合想用 AI 協助開發功能或修 bug,但又希望測試能緊扣透過公開介面的行為,而不是綁死在實作細節上的開發者。
tdd 的用途是什麼
當你希望模型做到以下事情時,就適合使用用於 Test Driven Development 的 tdd:
- 把功能規劃成小而完整的垂直切片
- 一次只寫一個失敗測試
- 只實作足以通過測試的最少程式碼
- 在 green 之後安全地重構
- 避免寫出在無害重構時也會壞掉的脆弱測試
哪些人最能從 tdd 獲益
如果你已經具備以下條件,tdd skill 會特別適合:
- 有一個可以執行測試的程式碼庫
- 有明確的公開 API、endpoint、command,或可操作的使用者流程可作為目標
- 有權限同時修改測試與正式程式碼
- 比起大量 mocking,更偏好 integration-style tests
它尤其適合 backend services、libraries、domain logic,以及那些可以透過真實介面來驗證行為的 application flows。
這個 tdd skill 與一般做法有什麼不同
它最關鍵的差異,在於對測試品質有非常明確的立場:
- 測行為,不測內部實作
- 優先採用 integration-style tests
- 只在 system boundaries 做 mock
- 避免「水平切片」做法,也就是先把所有測試寫完、之後才一次補所有程式碼
- 把 TDD 當成塑造更好介面的工具,而不只是增加 coverage 的手段
因此,tdd usage 比起一般只是要求 AI「把測試和實作都寫出來」的 prompt,更有紀律,也更能導向穩定的結果。
什麼情況會讓 tdd 不容易導入
如果你的環境無法執行測試、程式碼庫缺乏穩定的公開切入點,或團隊主要想要的是大量 snapshot 或 mock-heavy unit tests,那這個 skill 的適配度就會比較低。它也預設你願意用小步迭代,而不是一次就產出完整功能。
如何使用 tdd skill
在 skills 環境中安裝 tdd
如果你使用的是 repository 裡的 Skills system,常見的安裝方式是:
npx skills add mattpocock/skills --skill tdd
之後,在你要求模型以測試優先流程來實作或修正行為時,再呼叫 tdd。
大量使用 tdd 前,先讀這些檔案
想最快理解這個 skill,建議依序先看:
SKILL.mdtests.mdmocking.mdinterface-design.mdrefactoring.mddeep-modules.md
這個順序很重要。SKILL.md 提供的是操作哲學,而其餘檔案則進一步說明,在實務上什麼叫做「好的測試」與「好的設計」。
先掌握 tdd 預設的核心工作流程
這個 skill 是圍繞一個緊密迴圈打造的:
- 選定一個小而明確的行為
- 透過公開介面寫出一個失敗測試
- 只實作最少、足以通過的程式碼
- 在維持測試 green 的前提下重構
- 再進入下一個最小切片
如果你一開始就要求整個功能一次做完,tdd usage 的大部分價值其實就流失了。
從行為出發,不要從實作想像出發
好的輸入:
- 「Add checkout support for expired cards. Public entrypoint is
checkout(cart, paymentMethod). Existing test file ischeckout.test.ts. Keep using integration-style tests.」
較弱的輸入:
- 「Create classes for payment orchestration and add unit tests for each method.」
前者給的是明確的行為目標;後者則很容易把模型推向對內部設計的臆測,以及脆弱的測試寫法。
給 tdd skill 明確的公開介面與測試指令
想讓 tdd install 後的實際使用效果更好,請在 prompt 裡補齊以下資訊:
- 受測的 function、route、CLI command 或 UI action
- 測試檔案所在位置
- 使用的 test runner 與執行指令
- 相關限制,例如 DB、HTTP 或外部服務
- 哪些可以 mock、哪些不能 mock
一個實用的 prompt 範本:
Use the tdd skill.
Goal: Add [behavior].
Public interface: [function/route/command].
Test location: [path].
Run tests with: [command].
Boundaries to mock: [external API, clock, filesystem].
Do not mock: [internal modules/classes].
Work in red-green-refactor steps and explain each step briefly.
用垂直切片,不要用水平切片
這個 repository 最重要的實務收穫之一,就是不要一開始就把所有測試批量寫完。這裡所說的良好 tdd usage,意思是:
- 先挑一個真實情境
- 讓它先通過
- 再用這個結果來決定下一個情境
這樣能減少憑空想像的抽象設計,通常也會得到更好的 API 形狀。
在 tdd 中優先採用 integration-style tests
這個 repository 明確偏好:測試應透過公開 API 走真實程式路徑。實務上代表:
- 呼叫 exported functions,而不是 private helpers
- 透過支援的介面來打 route handlers
- 驗證可觀察的結果,而不是內部呼叫順序
- 用能力或成果來命名測試,例如「user can checkout with valid cart」
如果一次重構只改了內部實作、沒有改變行為,那好的測試通常應該繼續維持 green。
只在系統邊界做 mock
tdd skill 不是反對 mock,而是反對把自己的實作通通 mock 掉。適合 mock 的包括:
- payment gateways
- email providers
- time/randomness
- 視測試 setup 而定,有時也包含 databases 或 filesystem
不建議 mock 的包括:
- 你自己的 modules
- 內部協作元件
- private methods
- 你自己可控的 thin wrappers
光是這條原則,對輸出品質的提升往往就比大多數 prompt 微調更明顯。
在寫太多程式碼前,先把程式設計成容易測試
支援文件裡有個很重要的觀點:更好的介面,會讓 TDD 更容易落地。你可以要求模型偏好這類程式設計:
- 依賴項以接受注入的方式傳入,而不是在內部直接建立
- 回傳結果,而不是修改隱藏狀態
- 維持較小的公開 surface area
如果你目前的設計本身就很難測,請直接要求模型先提出一個更小、也更容易測試的公開介面。
一個強而有力的 tdd prompt 範例
Use the tdd skill to add password reset token expiry.
Context:
- Node + TypeScript
- Public API: `requestPasswordReset(email)` and `resetPassword(token, newPassword)`
- Tests: `src/auth/password-reset.test.ts`
- Run with: `pnpm test password-reset`
- Mock only email sending and time
- Do not mock repository code or internal services
Please:
1. choose the smallest failing behavior first
2. write integration-style tests through public APIs
3. implement minimum code to pass
4. refactor after green
5. avoid asserting internal call counts unless at an external boundary
這個 prompt 有效,是因為它同時給了 skill 明確目標、邊界規則,以及可實際執行的路徑。
留意 tdd skill 輸出品質的主要訊號
用這個 skill 產生出來的優質 tdd guide,通常會有以下特徵:
- 測試名稱圍繞使用者可見的行為
- 一次只處理一個小情境
- 每一步只寫最少實作
- green 之後會附帶重構說明
- 幾乎不依賴 private structure
反之,品質較差的結果通常會出現:
- 對內部程式碼大量使用 mock
- 驗證呼叫順序
- 第一步就塞進巨大測試套件
- 還沒讓任何行為通過前,就先堆出推測式抽象設計
tdd skill 常見問題
tdd 只適合全新的功能嗎?
不是。tdd skill 也很適合拿來處理 bug fixes。在很多成熟的程式碼庫裡,最佳第一步通常是先寫一個失敗的 regression test,透過公開介面重現 bug,接著做最小修正,最後再收尾整理。
這個 tdd skill 對新手友善嗎?
算是友善,前提是你已經知道怎麼跑專案測試。這套指引雖然有明確立場,但規則其實不複雜:測行為、切片要小、避免驗證實作細節。不過如果你是完全初學者,可能還是需要額外協助,才能理解自己專案的架構與測試工具鏈。
tdd 和直接要求 AI 寫測試,有什麼差別?
一般 prompt 很常產出偏 coverage 導向、或 mock 過重的測試。tdd skill 則會把模型推向:
- 以行為為中心的規格
- 更安全的重構空間
- 更乾淨的介面
- 更小、更可迭代的步驟
這不只會改變測試寫法,也會改變正式程式碼的設計。
什麼時候不該用 tdd?
以下情況可以先跳過,或只有限度地使用用於 Test Driven Development 的 tdd:
- 行為目前還無法被有意義地驗證
- 迭代時執行環境太難跑起來
- 你現在做的是探索性、可丟棄的 spike
- 任務主要是機械性變更,例如 rename 或 dependency bump
等到公開介面更清楚之後,仍然可以再回到 TDD。
tdd 是否只接受 integration tests?
不完全是,但它的偏好確實是透過真實介面來做 integration-style tests。目標不是把測試做得越大越好,而是在穩定的切入點上驗證行為。只要仍然停留在公開介面,且不綁定內部結構,小而聚焦的測試一樣是可以接受的。
哪些語言或框架適合這個 tdd skill?
這些理念大致上不綁語言。範例雖然偏向 TypeScript 與 JavaScript,但只要你的生態系能定義清楚的公開介面與測試邊界,Python、Java、Go、Ruby 等也都適用。
如何提升 tdd skill 的使用效果
給 tdd 一個更小的第一刀
想改善 tdd 結果,最簡單有效的方法,就是把第一步再縮小。不要從「build user invitations」開始,而是從「user with valid email can request an invitation」開始。切片越小,越能減少憑空腦補的架構,也越容易得到乾淨的測試。
明確提供邊界規則
很多糟糕輸出,根本原因都是 mocking policy 不夠清楚。請直接告訴模型:
- 哪些 external systems 可以 mock
- 哪些 internal modules 必須保持真實
- 是否有 test DB 可用
- time 應該以 injected 還是 frozen 方式處理
這會幫助 skill 更貼近 repository 的核心哲學。
要求以公開介面來命名測試
如果你想要更好的測試,請明確要求測試名稱描述結果:
- 好:
user can checkout with valid cart - 較弱:
checkout calls payment service
這一條指令,常常就足以避免測試逐漸滑向實作細節。
讓 red-green-refactor 明確可見
如果第一版回答一回來就是整包完整實作,請要求模型重整輸出格式:
- 先展示第一個 failing test
- 再展示足以通過的最少程式碼
- 把 refactor 拆開說明
- 必要時在第一個 slice 後就先停下來
tdd skill 在迴圈清楚可見時效果最好,而不是把過程都隱含起來。
當測試寫起來很彆扭時,優先改善設計
如果模型很難寫出乾淨的測試,問題往往不在 test syntax,而在 interface design。你可以要求它往以下方向調整:
- dependency injection
- explicit inputs and outputs
- 更小的 public surfaces
- 更少的 side effects
這也是 interface-design.md 和 deep-modules.md 特別有價值的地方。
把 refactoring 當成獨立的品質檢查階段
在幾個切片都 green 之後,可以明確要求做一次 refactor review,並依 repository 的線索檢查:
- duplication
- long methods
- shallow modules
- feature envy
- primitive obsession
這能避免 tdd usage 停留在「測試有過就好」,並在不改變行為的前提下提升可維護性。
及早修正常見失敗模式
如果輸出品質開始下滑,通常原因是:
- 在第一個 failing test 出現前就生成太多程式碼
- 對 internal collaborators 做了大量 mock
- 測試在驗證實作細節
- 功能需求對單一迴圈來說太大
- 公開介面不清楚,或每一步都在變
遇到這種情況時,最好重置成:一個行為、一個切入點、一個 failing test。
把 repository 檔案當成決策輔助工具
如果想要更好的結果,可以把自己的問題對應到支援文件:
- 測試風格偏弱時,看
tests.md - 邊界判斷不清楚時,看
mocking.md - 介面切點 awkward 時,看
interface-design.md - green 之後要整理時,看
refactoring.md - API shape 開始過寬時,看
deep-modules.md
這條閱讀路徑,比只快速掃過 SKILL.md 更有實際價值。
透過收緊限制來迭代,不要只重複同一句 prompt
如果第一次輸出表現普通,不要只是說一句「try again」。下一輪請用更具體的限制來修正:
- 只處理一個行為
- 保留目前的 public API
- 不要 mock internal modules
- 優先使用 test DB,而不是 repository mocks
- 在第一個 red-green-refactor cycle 後就停止
這種迭代方式,對提升 tdd guide 品質通常比單純要求「寫更詳細一點」更有效。
