بنقرة واحدة
test-driven-development
在实现任何 feature 或 bugfix 时使用;在写实现代码之前必须先写测试并看到它失败(TDD)。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
在实现任何 feature 或 bugfix 时使用;在写实现代码之前必须先写测试并看到它失败(TDD)。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
初始化新 skill 目录(运行 init_skill.py)、打包 skill(运行 package_skill.py)、或需要了解 skill 结构规范时使用。
在编写 skill 内容、验证 skill 是否有效、或需要用 TDD 方法测试 skill 能否被正确遵守时使用。
Frontend Master - 大师级前端页面开发。智能分析项目技术栈,生成独特设计美感的 UI,避免'AI审美'。自动持久化设计规范,保持项目一致性。整合 Frontend-Design 设计哲学 + UI-UX Pro Max 设计数据库。触发词: 前端、页面、组件、UI、登录页、落地页、dashboard、表单、卡片、导航栏。
你必须在任何创意工作之前使用:新功能、组件搭建、添加能力或修改行为。先通过对话澄清用户意图、需求与设计,再进入实现。
当你面对 2 个以上彼此独立的任务(无共享状态、无顺序依赖)时使用:为每个独立问题域派发一个 agent 并行推进。
当你已经有一份书面的 implementation plan,需要在独立 session 中按批执行并在批次间做 review checkpoint 时使用。
| name | test-driven-development |
| description | 在实现任何 feature 或 bugfix 时使用;在写实现代码之前必须先写测试并看到它失败(TDD)。 |
先写测试。看它失败。再写最小代码让它通过。
核心原则: 如果你没有亲眼看过测试失败,你就不知道它是否真的在测试正确的东西。
只遵守字面、不遵守精神,就是在违反规则。
永远:
例外(必须问 human partner):
如果你脑子里出现“就这一次跳过 TDD 吧”——停。那是在自我合理化。
没有先失败的测试,就禁止写 production code
先写了代码再写测试?删掉。重来。
没有例外:
从测试重新实现。就这样。
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="Next", shape=ellipse];
red -> verify_red;
verify_red -> green [label="yes"];
verify_red -> red [label="失败原因\n不对"];
green -> verify_green;
verify_green -> refactor [label="yes"];
verify_green -> green [label="no"];
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
确认:
测试失败? 修代码,不要修测试。
其他测试失败? 现在就修。
只有在全绿之后才做:
保持 tests 全绿。不要引入新行为。
为下一个功能点写下一个 failing test。
| 维度 | 好 | 坏 |
|---|---|---|
| Minimal | 一次只测一件事;名字里出现 “and”?拆开 | test('校验邮箱和域名和空白') |
| Clear | 名字描述行为 | test('test1') |
| Shows intent | 展示期望 API/用法 | 把期望行为藏起来 |
“我先写实现,之后再补测试验证”
事后写的测试通常会立刻通过;“立刻通过”证明不了任何事:
test-first 强迫你看到测试先失败,从而证明它确实在测试某个东西。
“我已经手动测过所有边界条件了”
手测是临时的、随机的:
自动化测试是系统化的,每次都一样地运行。
“删掉 X 小时的工作太浪费了”
沉没成本谬误。时间已经花掉了。你现在的选择是:
真正的浪费是保留“你无法信任”的代码。没有真实测试的“能跑”只是技术债。
“TDD 太教条,务实就该灵活点”
TDD 才是务实:
所谓“务实捷径”= 线上 debug = 更慢。
“事后测试也能达到同样目的——重要的是精神不是仪式”
不行。事后测试回答“它现在做了什么”;test-first 回答“它应该做什么”。
事后测试会被你的实现带偏:你会测试你写出来的东西,而不是需求要求的东西。你只会验证你记得的边界条件,而不是在实现前被迫发现的边界条件。
30 分钟的事后补测 ≠ TDD:你可能得到覆盖率,但失去“测试确实能抓 bug”的证明。
| 借口 | 现实 |
|---|---|
| “太简单了不值得测” | 简单代码也会坏;写个测试 30 秒。 |
| “我之后再测” | 立刻通过的测试证明不了任何事。 |
| “事后测试也一样” | 事后:what does this do;事前:what should this do。 |
| “我已经手测过了” | 临时 ≠ 系统;无记录、不可复跑。 |
| “删掉 X 小时太浪费” | 沉没成本;保留未验证代码是技术债。 |
| “留着当参考,先写测试” | 你会忍不住照着改;那就是事后测试。删就是删。 |
| “我得先探索一下” | 可以。探索完就丢掉,从 TDD 开始。 |
| “难测说明测试麻烦” | 难测 = 设计不清晰/难用;听测试的。 |
| “TDD 会拖慢我” | TDD 比 debug 更快;务实 = test-first。 |
| “手测更快” | 手测无法证明边界条件;每次改动都要重复测。 |
| “旧代码本来就没测试” | 你是在改进它;从你改的地方开始补测试。 |
看到这些就意味着:删掉代码。用 TDD 重来。
Bug: 空邮箱被接受
RED
test('拒绝空邮箱', async () => {
const result = await submitForm({ email: '' });
expect(result.error).toBe('需要邮箱');
});
Verify RED
$ npm test
FAIL: expected '需要邮箱', got undefined
GREEN
function submitForm(data: FormData) {
if (!data.email?.trim()) {
return { error: '需要邮箱' };
}
// ...
}
Verify GREEN
$ npm test
PASS
REFACTOR 如需要,可抽出多字段共享的 validation。
在标记“完成”之前逐项检查:
任何一项打不了勾?你跳过了 TDD。重来。
| 问题 | 解决方式 |
|---|---|
| 不知道怎么测 | 先写“你希望的 API”,先写断言;问 human partner。 |
| 测试太复杂 | 设计太复杂。简化接口。 |
| 必须 mock 一切 | 代码耦合太深。引入 dependency injection。 |
| 测试 setup 巨大 | 抽 helper;仍复杂就简化设计。 |
发现 bug?先写能复现的 failing test,然后走 TDD cycle。测试既证明修复有效,也防止 regression。
永远不要在没有测试的情况下修 bug。
当你要引入 mocks 或 test utilities 时,先读 @testing-anti-patterns.md,避免常见陷阱:
Production code → 必须先存在且先失败过的测试
否则 → 不是 TDD
除非 human partner 明确许可,否则没有例外。