Skip to content
Skills Library
Programming · Testing

test-driven-development

/test-driven-development

此技能应在实现任何功能或修复 bug 时使用,在编写实现代码之前。 它强制执行「红-绿-重构」循环,确保代码有测试覆盖且行为正确。

Licence
MIT
Last repository change
01.06.2026

Assessment and security

96/ 100Class: good
Format25 / 25
Frontmatter with name and description, valid name, description up to 1,024 characters, name matching the folder.
Discoverability21 / 25
Description explains when to use the skill, has a reasonable length and uses third person.
Content35 / 35
At least 150 words and at most 500 lines, headings, step lists, examples or code.
Hygiene15 / 15
No placeholders such as TODO, no Windows paths, with a licence.
Riskno signals detected in the scan
Files in this package
2
Scripts
0
HTML comments
0
URLs in SKILL.md
0
Deprecation

No signals detected. Repository changed on 01.06.2026, without references to retired models or APIs.

Licence

MIT, open: use and modify while retaining author attribution.

What it does and when to use it

此技能应在实现任何功能或修复 bug 时使用,在编写实现代码之前。 它强制执行「红-绿-重构」循环,确保代码有测试覆盖且行为正确。

Original author description

此技能应在实现任何功能或修复 bug 时使用,在编写实现代码之前。 它强制执行「红-绿-重构」循环,确保代码有测试覆盖且行为正确。

How it works

  1. 铁律
  2. 何时使用
  3. 红-绿-重构循环
  4. 好测试的特征
  5. 为什么顺序重要
  6. 常见合理化借口
  7. 当卡住时
  8. 危险信号 - 停下来重新开始
  9. 示例:Bug 修复
  10. 验证清单
Original SKILL.md headings · 333 lines, 615 words
SKILL.md preview
---
name: test-driven-development
description: |
  此技能应在实现任何功能或修复 bug 时使用,在编写实现代码之前。
  它强制执行「红-绿-重构」循环,确保代码有测试覆盖且行为正确。
disable-model-invocation: true
---

# 测试驱动开发(TDD)

> **核心理念**:如果没有看到测试失败,你不知道它是否测试了正确的东西。

## 铁律

```
没有失败测试,就没有生产代码
```

在测试之前写了代码?**删除它**。重新开始。

**没有例外:**
- 不要保留作为「参考」
- 不要在写测试时「改编」它
- 不要看它
- 删除就是删除

从测试开始全新实现。句号。

**违反规则的字面,就是违反规则的精神。**

---

## 何时使用

**始终使用:**
- 新功能
- Bug 修复
- 重构
- 行为变更

**例外(需要用户确认):**
- 一次性原型
- 生成的代码
- 配置文件

想着「就这一次跳过 TDD」?停下来。那是合理化借口。

---

## 红-绿-重构循环

```
┌─────────┐     ┌─────────┐     ┌─────────┐
│   红    │ ──→ │   绿    │ ──→ │  重构   │
│ 写失败  │     │ 最小代码│     │  清理   │
│  测试   │     │  通过   │     │         │
└─────────┘     └─────────┘     └─────────┘
     ↑                               │
     └───────────────────────────────┘
```

### 红 - 写失败测试

写一个最小的测试,展示应该发生什么。

**好的测试:**
```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);
});
```
清晰的名称,测试真实行为,只测一件事

**差的测试:**
```typescript
test('重试有效', async () => {
  const mock = jest.fn()
    .mockRejectedValueOnce(new Error())
    .mockResolvedValueOnce('success');
  await retryOperation(mock);
  expect(mock).toHaveBeenCalledTimes(2);
});
```
模糊的名称,测试 mock 而非代码

**要求:**
- 一个行为
- 清晰的名称
- 真实代码(除非无法避免才用 mock)

### 验证红 - 看它失败

**必须。绝不跳过。**

```bash
npm test path/to/test.test.ts
```

确认:
- 测试失败(不是报错)
- 失败消息符合预期
- 因为功能缺失而失败(不是拼写错误)

**测试通过了?** 你在测试已有行为。修复测试。

**测试报错了?** 修复错误,重新运行直到它正确失败。

### 绿 - 最小代码

写最简单的代码让测试通过。

**好的:**
```typescript
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');
}
```
刚好够通过

**差的:**
```typescript
async function retryOperation<T>(
  fn: () => Promise<T>,
  options?: {
    maxRetries?: number;
    backoff?: 'linear' | 'exponential';
    onRetry?: (attempt: number) => void;
  }
): Promise<T> {
  // YAGNI - 你不需要它
}
```
过度工程

不要添加功能、重构其他代码或「改进」超出测试要求的内容。

### 验证绿 - 看它通过

**必须。**

```bash
npm test path/to/test.test.ts
```

确认:
- 测试通过
- 其他测试仍然通过
- 输出干净(无错误、警告)

**测试失败了?** 修复代码,不是测试。

**其他测试失败了?** 现在就修复。

### 重构 - 清理

只在绿色之后:
- 消除重复
- 改进命名
- 提取辅助函数

保持测试绿色。不要添加行为。

### 重复

下一个失败测试测下一个功能。

---

## 好测试的特征

| 质量 | 好 | 差 |
|------|----|----|
| **最小** | 一件事。名称里有「和」?拆分它。 | `test('验证邮箱和域名和空白')` |
| **清晰** | 名称描述行为 | `test('test1')` |
| **显示意图** | 展示期望的 API | 模糊代码应该做什么 |

---

## 为什么顺序重要

**「我先写代码再写测试验证」**

代码之后写的测试立即通过。立即通过什么都不证明:
- 可能测错东西
- 可能测实现而非行为
- 可能遗漏你忘记的边缘情况
- 你从没看到它捕获 bug

测试优先强制你看到测试失败,证明它确实在测试某些东西。

**「删除 X 小时的工作太浪费了」**

沉没成本谬误。时间已经过去了。你现在的选择:
- 删除并用 TDD 重写(再花 X 小时,高置信度)
- 保留并之后加测试(30 分钟,低置信度,可能有 bug)

「浪费」是保留你无法信任的代码。没有真正测试的工作代码是技术债务。

---

## 常见合理化借口

| 借口 | 现实 |
|------|------|
| 「太简单不需要测试」 | 简单代码也会坏。测试只需 30 秒。 |
| 「我之后写测试」 | 立即通过的测试什么都不证明。 |
| 「之后写测试达到同样目的」 | 之后测试 =「这做了什么?」优先测试 =「这应该做什么?」 |
| 「已经手动测试过了」 | 临时 ≠ 系统化。无记录,无法重跑。 |
| 「保留作为参考,先写测试」 | 你会改编它。那就是之后测试。删除就是删除。 |
| 「TDD 会拖慢我」 | TDD 比调试更快。务实 = 测试优先。 |
| 「手动测试更快」 | 手动不能证明边缘情况。每次改动都要重测。 |

---

## 当卡住时

| 问题 | 解决方案 |
|------|----------|
| 不知道怎么测试 | 写理想 API。先写断言。问协作者。 |
| 测试太复杂 | 设计太复杂。简化接口。 |
| 必须 mock 所有东西 | 代码耦合太紧。使用依赖注入。 |
| 测试 setup 太庞大 | 提取辅助函数。还是复杂?简化设计。 |

---

## 危险信号 - 停下来重新开始

- 测试之前写了代码
- 实现之后写测试
- 测试立即通过
- 无法解释为什么测试失败
- 测试「之后」添加
- 合理化「就这一次」
- 「我已经手动测试过了」
- 「之后测试达到同样目的」
- 「保留作为参考」或「改编现有代码」
- 「已经花了 X 小时,删除太浪费」
- 「TDD 是教条主义,我在务实」
- 「这个情况不一样...」

**以上所有都意味着:删除代码。用 TDD 重新开始。**

---

## 示例:Bug 修复

**Bug:** 空邮箱被接受

**红**
```typescript
test('拒绝空邮箱', async () => {
  const result = await submitForm({ email: '' });
  expect(result.error).toBe('邮箱必填');
});
```

**验证红**
```bash
$ npm test
FAIL: expected '邮箱必填', got undefined
```

**绿**
```typescript
function submitForm(data: FormData) {
  if (!data.email?.trim()) {
    return { error: '邮箱必填' };
  }
  // ...
}
```

**验证绿**
```bash
$ npm test
PASS
```

**重构**
如果需要,为多个字段提取验证。

---

## 验证清单

标记工作完成之前:

- [ ] 每个新函数/方法都有测试
- [ ] 实现之前看到每个测试失败
- [ ] 每个测试因预期原因失败(功能缺失,不是拼写错误)
- [ ] 写了最小代码让每个测试通过
- [ ] 所有测试通过
- [ ] 输出干净(无错误、警告)
- [ ] 测试使用真实代码(只在无法避免时用 mock)
- [ ] 边缘情况和错误已覆盖

不能勾选所有框?你跳过了 TDD。重新开始。

---

## 与调试集成

发现 bug?写一个复现它的失败测试。遵循 TDD 循环。测试证明修复并防止回归。

绝不在没有测试的情况下修复 bug。

---

## 辅助材料

- [测试反模式](./references/testing-anti-patterns.md) - 避免常见陷阱

---

## 最终规则

```
生产代码 → 测试存在且先失败
否则 → 不是 TDD
```

没有用户许可不能例外。
Files in this package (2)
  • SKILL.md 8 KB
  • references/testing-anti-patterns.md 6 KB

Versions

RepositoryScoreLicence
This page · jerrylalala/compound-engineering96MIT
GanyuanRan/Aegis100MIT
lucasouzadev/agent-kit-skill100MIT
zeative/satgas100AGPL-3.0
nikospavlopoulos/devops-mutant-powers100MIT
JohnStrunk/CommonCast100LGPL
Sanit-mann/The-Insectoids100MIT
mastro993/fstack100MIT
jelvodev/yaap100MIT

Database snapshot: 13.11.2026 · package version 57e386b35b8a (SHA-256 digest of the entire package)

Downloads in the last 30 days: 0 · ranking

Report a problem with this skill: harmful content, incorrect descriptions or an author’s removal request.

Similar skills

  • 95community-test-driven-development
    Use when implementing any feature or bugfix, before writing implementation code. Enforces RED-GREEN-REFACTOR cycle with test-first approach.
  • 100tdd
    Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
  • 100tdd
    Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
  • 100tdd
    Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
  • 100tdd
    Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
  • 100tdd
    Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.

Feedback and reports

Your message goes to Bartosz Sokoliński. An email address is needed only for a reply. See the privacy policy.