with one click
dev-tdd
实现任何功能或修复 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
何时使用需要为编程项目、monorepo 或多级目录创建/更新 AGENTS.md、CLAUDE.md 软链接、项目级 agent 操作手册、子目录局部规则、验证命令和 Coding Agent 上下文边界时使用。
分析代码库结构并生成中文 token-lean 架构文档。
用于构建或维护个人 LLM 驱动的知识库。触发词:将资料导入 wiki、查询 wiki 知识、检查 wiki 质量、'添加到 wiki'、'我了解什么关于',或任何提到 'LLM wiki' 的场景。
当有书面实施计划要在单独会话中执行并带审查检查点时使用
用户明确要求隔离工作区、并行分支验证或临时试验时使用 - 创建隔离的 git worktree
开始开发编程相关对话时使用 - 建立如何查找和使用 skills,在做出任何响应前要求先检查适用的 skill
| name | dev-tdd |
| description | 实现任何功能或修复 bug 前使用,在编写实现代码之前 - 测试驱动开发 |
先写测试。看着它失败。编写最小代码使其通过。
核心原则: 如果你没有看着测试失败,你就不知道它是否测试了正确的东西。
违反规则的文字就是违反规则的精神。
始终:
例外(询问用户):
想"就这一次跳过 TDD"?停下来。那是合理化。
没有先有失败的测试,就没有生产代码
先写代码再写测试?删除它。重新开始。
没有例外:
从测试重新实现。就这样。
digraph tdd_cycle {
rankdir=LR;
red [label="红\n编写失败测试", shape=box, style=filled, fillcolor="#ffcccc"];
verify_red [label="验证失败\n正确", shape=diamond];
green [label="绿\n最小代码", shape=box, style=filled, fillcolor="#ccffcc"];
verify_green [label="验证通过\n全部绿色", shape=diamond];
refactor [label="重构\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;
}
编写一个最小测试展示应该发生什么。
好的例子:
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);
});
名称清晰,测试真实行为,一件事
坏的例子:
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
确认:
测试通过了? 你在测试已有行为。修复测试。
测试报错? 修复错误,重新运行直到正确失败。
编写最简单的代码使测试通过。
好的例子:
async function retryOperation<T>(fn: () => Promise<T>): Promise<T> {
for (let i = 0; i < 3; i++) {
try {
return await fn();
} catch (e) {
if (i === 2) throw e;
}
}
throw new Error('unreachable');
}
刚好够通过
坏的例子:
async function retryOperation<T>(
fn: () => Promise<T>,
options?: {
maxRetries?: number;
backoff?: 'linear' | 'exponential';
onRetry?: (attempt: number) => void;
}
): Promise<T> {
// YAGNI
}
过度工程
不要添加功能、重构其他代码,或"改进"超出测试范围。
强制。
npm test path/to/test.test.ts
确认:
测试失败? 修复代码,不是测试。
其他测试失败? 立即修复。
仅在绿之后:
保持测试绿。不要添加行为。
下一个失败测试对应下一个功能。
| 质量 | 好 | 坏 |
|---|---|---|
| 最小 | 一件事。名称中有"和"?拆分。 | test('验证邮箱和域名和空白') |
| 清晰 | 名称描述行为 | test('test1') |
| 展示意图 | 展示期望的 API | 模糊代码应该做什么 |
"我稍后写测试来验证它有效"
稍后写的测试立即通过。立即通过证明不了什么:
先测试迫使你看到测试失败,证明它确实测试了某些东西。
"我已经手动测试了所有边界情况"
手动测试是临时的。你以为你测试了所有东西但:
自动化测试是系统的。每次运行方式相同。
| 借口 | 现实 |
|---|---|
| "太简单不用测" | 简单代码也会坏。测试只需 30 秒。 |
| "我稍后测试" | 立即通过的测试证明不了什么。 |
| "稍后测试达到同样目标" | 稍后测试 = "这做什么?" 先测试 = "这应该做什么?" |
| "已经手动测过了" | 临时 ≠ 系统。无记录,无法重新运行。 |
| "删除 X 小时工作是浪费" | 沉没成本谬误。时间已经花了。选择:删除用 TDD 重写(高信心)vs 保留稍后加测试(低信心,可能有 bug)。 |
所有这些意味着:删除代码。用 TDD 重新开始。
标记工作完成前:
无法勾选所有?你跳过了 TDD。重新开始。
| 问题 | 解决方案 |
|---|---|
| 不知道怎么测试 | 写期望的 API。先写断言。询问用户。 |
| 测试太复杂 | 设计太复杂。简化接口。 |
| 必须 mock 所有东西 | 代码太耦合。用依赖注入。 |
| 测试 setup 庞大 | 提取辅助函数。还复杂?简化设计。 |
在系统化调试中,找到根本原因后,必须使用 dev-tdd:
dev-tdd skill 编写正确的失败测试永远不要无测试修复 bug。
编写计划时,每个任务的步骤应体现 TDD:
执行计划中的每个任务时:
生产代码 → 测试存在且先失败
否则 → 不是 TDD
未经用户许可没有例外。