一键导入
test-driven-development
Use when implementing any feature or bug fix, before writing implementation code
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when implementing any feature or bug fix, before writing implementation code
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Before any creative work — creating features, building components, adding capabilities, or modifying behavior — you must use this skill. Explore user intent, requirements, and design before implementation.
Use when executing implementation plans with independent tasks in the current session
Use when creating new skills, editing existing skills, or verifying skills work before deployment
Use when facing 2+ independent tasks that can be worked on without shared state or sequential dependencies
Use when you have a written implementation plan to execute in a separate session with review checkpoints
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
| name | test-driven-development |
| description | Use when implementing any feature or bug fix, before writing implementation code |
先写测试。看着它失败。编写最少的代码让它通过。
核心原则: 如果你没有看着测试失败,你就不知道它是否测试了正确的东西。
违反规则的文字就是违反规则的精神。
始终:
例外(询问你的合作伙伴):
认为"这次跳过 TDD"?停止。这是合理化。
在没有首先失败测试的情况下,不要写生产代码
在测试之前写代码?删除它。重新开始。
没有例外:
根据测试重新开始。就是这样。
digraph tdd_cycle {
rankdir=LR;
red [label="RED\n编写失败的测试", shape=box, style=filled, fillcolor="#ffcccc"];
verify_red [label="验证失败\n正确", shape=diamond];
green [label="GREEN\n最少的代码", shape=box, style=filled, fillcolor="#ccffcc"];
verify_green [label="验证通过\n全部绿色", shape=diamond];
refactor [label="REFACTOR\n清理", shape=box, style=filled, fillcolor="#ccccff"];
next [label="下一个", shape=ellipse];
red -> verify_red;
verify_red -> green [label="是"];
verify_red -> red [label="错误\n失败"];
green -> verify_green;
verify_green -> refactor [label="是"];
verify_green -> green [label="否"];
refactor -> verify_green [label="保持\n绿色"];
verify_green -> next;
next -> red;
}
写一个最少的测试显示应该发生什么。
```typescript test('重试失败的操作3次', 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); });
清晰的名称,测试真实行为,一件事
</Good>
<Bad>
```typescript
test('重试工作', async () => {
const mock = jest.fn()
.mockRejectedValueOnce(new Error())
.mockRejectedValueOnce(new Error())
.mockResolvedValueOnce('success');
await retryOperation(mock);
expect(mock).toHaveBeenCalledTimes(3);
});
模糊的名称,测试 mock 而不是代码
要求:
强制。永远不要跳过。
npm test path/to/test.test.ts
确认:
测试通过? 你在测试现有行为。修复测试。
测试错误? 修复错误,重新运行直到正确失败。
编写最简单的代码让测试通过。
```typescript async function retryOperation(fn: () => Promise): Promise { for (let i = 0; i < 3; i++) { try { return await fn(); } catch (e) { if (i === 2) throw e; } } throw new Error('unreachable'); } ``` 仅仅通过 ```typescript async function retryOperation( fn: () => Promise, options?: { maxRetries?: number; backoff?: 'linear' | 'exponential'; onRetry?: (attempt: number) => void; } ): Promise { // YAGNI } ``` 过度工程不要添加功能、重构其他代码或"改进"超过测试。
强制。
npm test path/to/test.test.ts
确认:
测试失败? 修复代码,不是测试。
其他测试失败? 现在修复。
仅在绿色之后:
保持测试绿色。不要添加行为。
下一个失败测试用于下一个功能。
| 质量 | 好 | 坏 |
|---|---|---|
| 最少 | 一件事。名称中有"and"?分开它。 | test('validates email and domain and whitespace') |
| 清晰 | 名称描述行为 | test('test1') |
| 显示意图 | 展示期望的 API | 隐藏代码应该做什么 |
"我之后写测试来验证它工作"
测试编写后立即通过。立即通过不能证明什么:
测试优先强制你看到测试失败,证明它实际上测试了一些东西。
"我已经手动测试了所有边缘情况"
手动测试是随意的。你认为你测试了一切但:
自动化测试是系统化的。它们每次以相同的方式运行。
"删除 X 小时的工作是浪费"
沉没成本谬误。时间已经过去了。你现在的选择是:
"浪费"是保留你不能信任的代码。没有真正测试的工作代码是技术债务。
"TDD 是教条的,实用意味着适应"
TDD 是实用的:
"实用"捷径 = 在生产中调试 = 更慢。
"之后的测试达到相同的目标——是精神而不是仪式"
不。之后的测试回答"这做什么?"测试优先回答"这应该做什么?"
之后的测试被你的实现所偏见。你测试你构建的,而不是要求的。你验证记住的边缘情况,而不是发现的。
测试优先在实施之前强制发现边缘情况。之后测试验证你记住了 everything(你没有)。
30 分钟之后的测试 ≠ TDD。你得到覆盖率,失去测试工作的证明。
| 借口 | 现实 |
|---|---|
| "太简单不需要测试" | 简单的代码也会坏。测试只需要 30 秒。 |
| "我之后测试" | 立即通过的测试不能证明什么。 |
| "之后的测试达到相同目标" | 之后的测试 = "这做什么?" 测试优先 = "这应该做什么?" |
| "已经手动测试过了" | 随意 ≠ 系统化。没有记录,无法重新运行。 |
| "删除 X 小时是浪费" | 沉没成本谬误。保留无法验证的代码是技术债务。 |
| "保留作为参考,先写测试" | 你会改编它。那是之后测试。删除意味着删除。 |
| "需要先探索" | 没关系。丢弃探索,从 TDD 开始。 |
| "测试难 = 设计不清楚" | 倾听测试。难测试 = 难使用。 |
| "TDD 会让我变慢" | TDD 比调试更快。实用 = 测试优先。 |
| "手动测试更快" | 手动不能证明边缘情况。每次更改你都会重新测试。 |
| "现有代码没有测试" | 你正在改进它。为现有代码添加测试。 |
所有这些意味着:删除代码。用 TDD 重新开始。
Bug: 接受空邮箱
RED
test('拒绝空邮箱', async () => {
const result = await submitForm({ email: '' });
expect(result.error).toBe('Email required');
});
验证 RED
$ npm test
FAIL: expected 'Email required', got undefined
GREEN
function submitForm(data: FormData) {
if (!data.email?.trim()) {
return { error: 'Email required' };
}
// ...
}
验证 GREEN
$ npm test
PASS
REFACTOR 如需要,为多个字段提取验证。
在标记工作完成之前:
无法勾选所有方框?你跳过了 TDD。重新开始。
| 问题 | 解决方案 |
|---|---|
| 不知道如何测试 | 编写期望的 API。首先编写断言。询问你的合作伙伴。 |
| 测试太复杂 | 设计太复杂。简化接口。 |
| 必须 mock 一切 | 代码太耦合。使用依赖注入。 |
| 测试设置巨大 | 提取辅助函数。还复杂?简化设计。 |
发现 bug?编写重现它的失败测试。遵循 TDD 周期。测试证明修复并防止回归。
永远不要在没有测试的情况下修复 bug。
添加 mock 或测试工具时,阅读 @testing-anti-patterns.md 以避免常见陷阱:
生产代码 → 测试存在且首先失败
否则 → 不是 TDD
没有例外,除非你获得合作伙伴的许可。