tdd 是一项面向 Test Driven Development 的技能,强调严格执行 red-green-refactor,通过公共接口编写以行为为中心的测试,优先采用集成风格测试,并仅在系统边界使用 mocking。
该技能评分为 78/100,说明它是一个较可靠的目录收录项:用户通常可以期待智能体识别何时该调用它,并获得比通用提示更有针对性的 TDD 指导,但不应期待它提供一套完整脚本化的端到端工作流。
- frontmatter 的触发信号很强:明确提到了 TDD、red-green-refactor、integration tests,以及 test-first 的使用线索。
- 对“该测什么、不该测什么”给出了较好的操作性指导,并在 `tests.md` 和 `mocking.md` 中提供了具体示例。
- 补充了不少有助于正确执行的关键理念,包括 interface design、deep modules、mocking boundaries,以及每轮结束后的重构提示。
- `SKILL.md` 中没有提供 quick start 或安装/执行说明,因此智能体需要在会话中自行判断如何落地使用。
- 内容以原则和示例为主,缺少针对真实功能开发或 bug 修复的分步式 TDD 演练流程。
tdd skill 概览
tdd skill 是一份聚焦于测试先行开发的实战指南,强调严格的 red-green-refactor 循环,而不是那种泛泛的“顺手写点测试”的提示词。它尤其适合希望借助 AI 来开发新功能或修复 bug 的开发者:在推进实现时,始终让测试围绕通过公共接口可观察到的行为,而不是盯着实现细节不放。
tdd 适合解决什么问题
当你希望模型做到以下几点时,就应该使用 tdd for Test Driven Development:
- 把一个功能拆成小而完整的纵向切片
- 一次只写一个失败测试
- 只实现刚好能通过测试的最少代码
- 变绿后再安全重构
- 避免那些一做无害重构就碎掉的脆弱测试
哪类人最能从 tdd 中获益
如果你已经具备以下条件,tdd skill 往往最适合你:
- 现有代码库里的测试可以实际跑起来
- 有明确的公共 API、endpoint、命令或用户流程可作为目标
- 你有权限同时修改测试代码和生产代码
- 你更倾向 integration-style tests,而不是大量 mocking
它尤其适用于 backend service、library、领域逻辑,以及那些可以通过真实接口来驱动和验证行为的应用流程。
这个 tdd skill 的差异点在哪里
这个 skill 最核心的区别,在于它对测试质量有一套很明确的立场:
- 测行为,不测内部实现
- 优先 integration-style tests
- 只在系统边界做 mock
- 避免“横向切片”——先把所有测试写完、再把所有代码补完
- 用 TDD 塑造更好的接口,而不只是补 coverage
也正因如此,tdd 的使用方式会比普通的“让 AI 写测试和实现”提示词更有纪律性、更可控。
哪些情况会影响采用 tdd
如果你的环境根本跑不了测试、代码库没有稳定的公共接缝,或者团队主要偏好 snapshot 很重、mock 很多的 unit test,这个 skill 的适配度就会弱一些。它也默认你愿意按小步迭代推进,而不是一口气生成整个功能。
如何使用 tdd skill
在技能环境中安装 tdd
如果你使用的是该仓库里的 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 的大部分价值其实就丢掉了。
从行为出发,而不是从实现想象出发
强输入示例:
- “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.”
第一种提示给的是明确的行为目标;第二种则会把模型推向对内部设计的猜测,以及更容易脆化的测试。
给 skill 明确的公共接口和测试命令
想获得更好的 tdd install 与执行效果,输入里最好包含:
- 被测试的 function、route、CLI command 或 UI action
- 测试代码所在位置
- 使用的 test runner 和运行命令
- 相关约束,比如 DB、HTTP 或外部服务
- 哪些可以 mock,哪些不能 mock
一个实用的提示模板如下:
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.
用纵向切片,不要用横向切片
这个仓库最值得带走的实践之一,就是不要一开始就把所有测试批量写完。这里所说的优质 tdd 用法,更像是:
- 先挑一个真实场景
- 先让它通过
- 再让这个结果指导下一个场景怎么写
这样做能减少想象出来的抽象,通常也会得到更自然的 API 形态。
在 tdd 中优先选择 integration-style tests
该仓库明显更偏向通过公共 API 跑通真实代码路径的测试。落到实践上,通常意味着:
- 调用导出的函数,而不是 private helper
- 通过框架支持的接口去触发 route handler
- 验证可观察结果,而不是内部调用顺序
- 按能力命名测试,比如“user can checkout with valid cart”
如果一次重构只改了内部实现、外部行为没变,那么一个好的测试通常应该继续保持 green。
只在系统边界做 mock
tdd skill 不是反对 mock,而是反对把你自己的实现也 mock 掉。适合 mock 的包括:
- payment gateway
- email provider
- time / randomness
- 某些情况下的 database 或 filesystem,具体取决于测试环境
不应该 mock 的包括:
- 你自己的 module
- 内部协作者
- private method
- 你自己可控的薄封装
光是这条原则,对输出质量的提升通常就比大多数提示词微调更明显。
先把代码设计成更易测试,再继续写太多代码
配套文件里有个很关键的观点:接口设计越好,TDD 就越容易推进。可以明确要求模型优先写出这样的代码:
- 通过参数接收依赖,而不是在内部直接创建
- 返回结果,而不是偷偷修改隐藏状态
- 让公共 surface area 保持小而清晰
如果你当前的设计天然不利于测试,可以先让模型提出一个更小、更容易测试的公共接口,再继续往下做。
一个强而有效的 tdd 提示示例
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
它之所以有效,是因为它同时给出了明确目标、边界规则,以及真实可执行的路径。
观察 tdd 输出质量时,重点看这些信号
一个好的 tdd guide 输出,通常会有这些特征:
- 测试名称围绕用户可见行为
- 一次只处理一个小场景
- 每一步只做最少实现
- green 之后会补充重构说明
- 几乎不依赖 private structure
而较差的输出通常会出现:
- 对内部代码大量 mock
- 大量断言调用顺序
- 第一步就塞进巨大的测试套件
- 在任何行为通过之前就先造一堆抽象
tdd skill 常见问题
tdd 只适合全新功能吗?
不是。tdd skill 同样很适合修 bug。在很多成熟代码库里,最好的第一步往往是:先通过公共接口写出能稳定复现问题的失败回归测试,再做最小修复,最后清理和重构。
这个 tdd skill 对新手友好吗?
友好,前提是你已经知道怎么运行项目测试。它的指导思路虽然有明确倾向,但并不复杂:测试行为、切片要小、不要断言实现细节。不过如果你是完全初学者,可能仍然需要额外帮助来理解项目架构和测试工具链。
tdd 和直接让 AI 写测试,有什么区别?
普通提示词往往会产出偏 coverage 导向、或者 mock 很重的测试。而 tdd skill 会把模型往这些方向推:
- 行为规格
- 更安全的重构
- 更干净的接口
- 更小的迭代步幅
这不仅会改变测试写法,也会反过来影响生产代码的设计质量。
什么时候不该用 tdd?
在以下场景下,应该跳过或限制 tdd for Test Driven Development 的使用:
- 行为暂时还无法被有意义地驱动和验证
- 环境太难在迭代过程中跑起来
- 你现在做的是探索性质、会被丢弃的 spike
- 任务主要是机械性修改,比如重命名或升级依赖
等公共接缝更清晰以后,你仍然可以再回到 TDD。
tdd 是否只适用于 integration tests?
也不完全是,但它明显偏向通过真实接口来做 integration-style tests。重点不是让测试越大越好,而是在一个稳定的接缝上验证行为。只要测试仍然围绕公共接口、没有和内部实现耦死,小而聚焦的测试同样没问题。
哪些语言或框架适合这个 skill?
这些理念基本与语言无关。示例会更偏 TypeScript 和 JavaScript,但只要你的生态里能定义清晰的公共接口和测试边界,这套设计原则同样适用于 Python、Java、Go、Ruby 等语言。
如何提升 tdd skill 的效果
给 tdd 一个更小的第一步
提升 tdd 输出质量最简单的方法,就是把第一步再缩小。不要一开始就说“build user invitations”,而是先从“user with valid email can request an invitation”开始。切片越小,越能减少模型幻想出来的架构,也更容易得到干净的测试。
明确给出边界规则
很多糟糕输出,本质上都来自 mocking 策略不清楚。直接告诉模型:
- 哪些外部系统可以 mock
- 哪些内部模块必须保持真实
- 是否有 test DB 可用
- time 应该通过注入处理,还是直接冻结
这样更容易让这个 skill 保持与仓库哲学一致。
要求测试名称围绕公共接口行为
如果你想得到更好的测试,可以直接要求测试名称描述结果:
- good:
user can checkout with valid cart - weaker:
checkout calls payment service
很多时候,就这一条指令,就足以避免测试一路滑向实现细节。
让 red-green-refactor 明确可见
如果模型第一次回复直接变成一整坨完整实现,就让它重排输出结构:
- 先展示第一个失败测试
- 再展示刚好能通过的最小代码
- 把重构说明单独说清楚
- 必要时在一个切片后就停下
tdd skill 在循环清晰可见时效果最好,而不是把流程默认藏起来。
当测试写起来别扭时,优先改设计
如果模型很难写出干净测试,问题往往不在测试语法,而在接口设计。可以要求它把设计往这些方向调整:
- dependency injection
- 明确的输入和输出
- 更小的公共 surface
- 更少的副作用
这也是为什么 interface-design.md 和 deep-modules.md 会特别有价值。
把重构单独当成一次质量检查
当你已经跑通几个 green 切片后,可以明确要求它基于仓库里的标准做一次 refactor review,重点看:
- duplication
- long methods
- shallow modules
- feature envy
- primitive obsession
这样可以避免 tdd 的使用停留在“测试通过就算完”,而是在不改变行为的前提下,进一步提升可维护性。
尽早纠正常见失败模式
如果输出质量开始下滑,常见原因通常是:
- 在第一个失败测试之前就生成了太多代码
- 对内部协作者做了大量 mock
- 测试在验证实现细节
- 功能请求体量太大,超出一个循环能承载的范围
- 公共接口不清晰,或者每一步都在变
一旦出现这种情况,就重置回:一个行为、一个接缝、一个失败测试。
把仓库文件当成决策辅助,而不只是背景资料
想把效果做得更好,可以把问题直接映射到相应支持文档:
- 测试风格不对,就看
tests.md - 边界该怎么定不清楚,就看
mocking.md - 接缝 awkward,就看
interface-design.md - 变绿后要怎么整理,就看
refactoring.md - API shape 越做越宽,就看
deep-modules.md
相比只粗略扫一眼 SKILL.md,这样的阅读路径实际价值更高。
通过收紧约束来迭代,而不是重复同一句提示
如果第一轮输出一般,不要只说一句“try again”。更有效的做法,是在下一轮加入更具体的约束:
- 只针对一个行为
- 保持当前 public API 不变
- 不要 mock internal modules
- 优先使用 test DB,而不是 repository mocks
- 在第一个 red-green-refactor 循环后停止
这种迭代方式,对提升 tdd guide 质量通常比单纯要求“写得更详细”更有效。
