with one click
test-driven-development
在实现任何功能或修复 bug 时使用,在编写实现代码之前
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
在实现任何功能或修复 bug 时使用,在编写实现代码之前
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
桌面/网关运行时内置的计划创建技能,用于生成可落盘、可调度、可批次执行的结构化计划包。
桌面/网关运行时内置的计划执行技能,用于按批次执行结构化计划包中的单个任务文件。
Use when demonstrating or verifying VibeWindow local plugin packaging, including plugin skills, MCP servers, hook declarations, and interface metadata.
当需要在编码前创建或更新实施计划时使用,尤其适用于多步骤功能开发、重构、包含多个活动部件的缺陷修复,或需要拆分为可独立执行并跟踪进度的任务文件的请求。
当你有书面实现计划需要在单独会话中执行,并带有审查检查点时使用
通过 `rustcodegraph` 命令行界面使用 RustCodeGraph 理解、导航或脚本化操作已索引代码库。当用户要求使用 RustCodeGraph、需要高性能搜索检索代码、需要符号/源码/调用流上下文、调用方/被调用方/影响分析或受影响测试选择时使用。
| name | test-driven-development |
| description | 在实现任何功能或修复 bug 时使用,在编写实现代码之前 |
先写测试。看它失败。写最少的代码让它通过。
核心原则: 如果你没有看到测试失败,你就不知道它是否测试了正确的东西。
违反规则的字面意思就是违反规则的精神。
总是使用:
例外情况(请询问你的人类搭档):
想着"就这一次跳过 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
确认:
测试失败了? 修正代码,不是测试。
其他测试失败了? 立即修正。
只在变绿之后:
保持测试绿色。不要添加行为。
为下一个功能写下一个失败的测试。
| 品质 | 好的 | 坏的 |
|---|---|---|
| 最小化 | 只测一件事。名称中有"和"?拆分它。 | test('验证邮箱和域名和空格') |
| 清晰 | 名称描述行为 | test('测试1') |
| 展示意图 | 展示期望的 API | 模糊代码应该做什么 |
"我先写测试后验证它能工作"
代码之后写的测试会立即通过。立即通过说明不了什么:
先写测试迫使你看到测试失败,证明它确实在测试某些东西。
"我已经手动测试了所有边界情况"
手动测试是临时的。你以为测试了所有东西,但是:
自动化测试是系统化的。它们每次都以相同方式运行。
"删除 X 小时的工作太浪费了"
沉没成本谬误。时间已经过去了。你现在的选择:
"浪费"是保留你不能信任的代码。没有真正测试的工作代码就是技术债务。
"TDD 是教条主义的,务实意味着灵活变通"
TDD 本身就是务实的:
"务实"的捷径 = 在生产环境调试 = 更慢。
"事后测试能达到同样目标 - 重要的是精神不是仪式"
不。事后测试回答"这做了什么?"先写测试回答"这应该做什么?"
事后测试受你的实现偏见影响。你测试的是你构建的东西,而不是需求。你验证的是你记住的边界情况,而不是发现的边界情况。
先写测试迫使你在实现之前发现边界情况。事后测试验证你记住了所有东西(你并没有)。
30 分钟的事后测试 ≠ TDD。你获得了覆盖率,但失去了测试有效的证明。
| 借口 | 现实 |
|---|---|
| "太简单不需要测试" | 简单的代码也会出 bug。测试只需要30秒。 |
| "我之后再测试" | 测试立即通过证明不了什么。 |
| "事后测试达到同样目标" | 事后测试 = "这做了什么?" 先写测试 = "这应该做什么?" |
| "已经手动测试过了" | 临时测试 ≠ 系统化测试。没有记录,无法重新运行。 |
| "删除 X 小时的工作太浪费" | 沉没成本谬误。保留未验证的代码才是技术债务。 |
| "保留作为参考,先写测试" | 你会调整它。那就是事后测试。删除就是删除。 |
| "需要先探索" | 可以。扔掉探索结果,用 TDD 开始。 |
| "测试很难写 = 设计不清晰" | 听测试的。难测试 = 难使用。 |
| "TDD 会让我变慢" | TDD 比调试更快。务实 = 先写测试。 |
| "手动测试更快" | 手动测试证明不了边界情况。每次改动都要重新测试。 |
| "现有代码没有测试" | 你正在改进它。为现有代码添加测试。 |
以上所有意味着:删除代码。用 TDD 从头开始。
Bug: 空邮箱被接受
RED
test('拒绝空邮箱', async () => {
const result = await submitForm({ email: '' });
expect(result.error).toBe('邮箱必填');
});
验证 RED
$ npm test
FAIL: expected '邮箱必填', got undefined
GREEN
function submitForm(data: FormData) {
if (!data.email?.trim()) {
return { error: '邮箱必填' };
}
// ...
}
验证 GREEN
$ npm test
PASS
REFACTOR 如果需要,为多个字段提取验证逻辑。
在标记工作完成之前:
不能勾选所有选项?你跳过了 TDD。从头开始。
| 问题 | 解决方案 |
|---|---|
| 不知道如何测试 | 写出期望的 API。先写断言。询问你的人类搭档。 |
| 测试太复杂 | 设计太复杂。简化接口。 |
| 必须 mock 所有东西 | 代码耦合太紧。使用依赖注入。 |
| 测试设置很庞大 | 提取辅助函数。仍然复杂?简化设计。 |
发现 bug?写一个复现它的失败测试。遵循 TDD 循环。测试证明修复并防止回归。
永远不要在没有测试的情况下修复 bug。
当添加 mock 或测试工具时,阅读 @testing-anti-patterns.md 以避免常见陷阱:
生产代码 → 测试存在且先失败
否则 → 不是 TDD
没有你的人类搭档的许可,没有例外。