Use when implementing any feature or bugfix, before writing implementation code - write the test first, watch it fail, write minimal code to pass; ensures tests actually verify behavior by requiring failure first. Enforces RED-GREEN-REFACTOR cycle with iron-law compliance.
Install via CLI
openskills install huangwb8/skills---
name: tdd-workflow
description: Use when implementing any feature or bugfix, before writing implementation code - write the test first, watch it fail, write minimal code to pass; ensures tests actually verify behavior by requiring failure first. Enforces RED-GREEN-REFACTOR cycle with iron-law compliance.
metadata:
short-description: TDD 测试驱动开发工作流
keywords:
- tdd-workflow
- TDD
- 测试驱动开发
- Red-Green-Refactor
- 测试先行
- 单元测试
- 测试覆盖率
- test-first
- test-driven
category: 测试
author: Bensz Conan
platform: Claude Code | OpenAI Codex | ChatGPT
iron-law: |
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
---
# TDD Workflow - 测试驱动开发工作流
## 与 bensz-collect-bugs 的协作约定
- 因本 skill 设计缺陷导致的 bug,先用 `bensz-collect-bugs` 规范记录到 `~/.bensz-skills/bugs/`,不要直接修改用户本地已安装的 skill 源码;若有 workaround,先记 bug,再继续完成任务。
- 只有用户明确要求“report bensz skills bugs”等公开上报时,才用本地 `gh` 上传新增 bug 到 `huangwb8/bensz-bugs`;不要 pull / clone 整个仓库。
## 铁律
```
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
```
**违反规则的信件就是违反规则的精神。**
**无例外**:
- 不保留为"参考"
- 写测试时"不调整"
- 不看它
- 删除=删除
---
## 常见合理化
| 借口 | 现实 |
|------|------|
| "太简单不需要测试" | 简单代码也会坏。测试只需 30 秒 |
| "我之后再测试" | 测试立即通过证明不了什么 |
| "测试后达到相同目的" | 测试后="代码做什么?"测试前="代码应该做什么?" |
| "已经手动测试了" | 临时≠系统化。无记录,无法重新运行 |
| "删除 X 小时工作是浪费" | 沉没成本谬误。保留未验证代码是技术债 |
| "保留参考,先写测试" | 你会调整它。那是测试后。删除=删除 |
---
## 红色标志 - 停止并重新开始
- 测试前有代码
- "已经手动测试了"
- "测试后达到相同目的"
- "是精神而非仪式"
- "只此一次"的合理化
- "保留为参考"
- 写测试时"不调整"
**所有这些意味着:删除代码。用 TDD 重新开始。**
---
## 核心理念
**测试驱动开发(Test-Driven Development, TDD)** 是一种先编写测试,再编写实现代码的开发方法。通过严格的 **Red-Green-Refactor** 循环,确保代码质量和可维护性。
### TDD 循环
```
┌─────────────────────────────────────────────────────────┐
│ 1. RED : 编写失败的测试 │
│ 2. GREEN : 编写最简单的代码使测试通过 │
│ 3. REFACTOR: 在测试保护下重构代码 │
│ 4. 重复循环 │
└─────────────────────────────────────────────────────────┘
```
---
## 何时使用本技能
在以下场景时激活:
- 用户明确要求使用 **TDD** 或 **测试驱动开发**
- 需要编写新功能或修复 Bug
- 提到"测试"、"单元测试"、"测试覆盖率"
- 需要确保代码质量
- 重构现有代码(先补充测试)
---
## TDD 工作流程
### 步骤 1:理解需求
在开始编码前,明确:
- **功能需求**:这个功能要做什么?
- **验收标准**:如何判断功能正确?
- **边界条件**:有哪些特殊情况?
- **错误处理**:异常情况如何处理?
### 步骤 2:编写失败测试(RED)
**测试先行原则**:
1. **先写测试,不写实现**
2. **运行测试,确认失败**(证明测试有效)
3. **阅读错误信息,理解预期**
**测试命名规范**(AAA 模式):
```python
# Should_预期行为_When_测试条件
def should_return_user_when_id_exists():
# Arrange(准备)
user_id = 123
expected_user = User(id=123, name="Alice")
# Act(执行)
result = user_service.get_by_id(user_id)
# Assert(断言)
assert result.id == expected_user.id
assert result.name == expected_user.name
```
### 步骤 3:最小化实现(GREEN)
**最简单的可工作代码**:
- **只写足够使测试通过的代码**
- **不追求完美,追求通过**
- **硬编码可以接受**(第一步)
```python
# 最初版本 - 硬编码也可以
def get_by_id(user_id):
if user_id == 123:
return User(id=123, name="Alice")
return None
```
### 步骤 4:运行测试确认通过
```bash
# 运行测试
pytest tests/test_user_service.py -v
# 期望输出
✅ should_return_user_when_id_exists PASSED
```
### 步骤 5:重构代码(REFACTOR)
**在测试保护下优化**:
- **消除重复**
- **提取方法**
- **改善命名**
- **优化结构**
```python
# 重构后版本
def get_by_id(user_id):
return _user_repository.find_by_id(user_id)
```
### 步骤 6:重复循环
每个功能点重复上述步骤,直到功能完整。
---
## 测试质量标准
### 必须遵守的规则
- ✅ **测试覆盖率 ≥ 80%**
- ✅ **每个测试用例独立**(不依赖其他测试)
- ✅ **测试可重复**(多次运行结果一致)
- ✅ **测试命名清晰**(描述意图)
- ✅ **遵循 AAA 模式**(Arrange-Act-Assert)
### 禁止的反模式
- ❌ **伪测试**:测试代码没有断言
- ❌ **万能测试**:一个测试验证太多东西
- ❌ **测试内部实现**:应该测试行为,不是实现细节
- ❌ **脆弱测试**:依赖外部状态(时间、随机数等)
---
## TDD 最佳实践
### 1. 小步前进
- **一次只写一个测试**
- **一次只实现一个功能点**
- **频繁运行测试**(每 1-2 分钟)
### 2. 测试隔离
```python
# 好的示例 - 使用 fixtures
@pytest.fixture
def clean_database():
db.reset()
yield
db.cleanup()
def test_create_user(clean_database):
user = user_service.create("Alice")
assert user.name == "Alice"
```
### 3. 测试边界条件
```python
def test_get_by_id():
# 正常情况
assert get_user(1) is not None
# 边界条件
assert get_user(0) is None
assert get_user(-1) is None
assert get_user(999999) is None
```
### 4. 测试异常情况
```python
def test_create_user_with_duplicate_email():
with pytest.raises(DuplicateEmailError):
user_service.create("alice@example.com")
user_service.create("alice@example.com")
```
---
## 不同语言的 TDD 示例
### Python (pytest)
```python
# 测试
def should_calculate_total_price():
cart = ShoppingCart()
cart.add_item(Item(name="Book", price=10))
cart.add_item(Item(name="Pen", price=5))
assert cart.total_price() == 15
# 实现
class ShoppingCart:
def __init__(self):
self.items = []
def add_item(self, item):
self.items.append(item)
def total_price(self):
return sum(item.price for item in self.items)
```
### JavaScript (Jest)
```javascript
// 测试
test('should calculate total price', () => {
const cart = new ShoppingCart();
cart.addItem({ name: 'Book', price: 10 });
cart.addItem({ name: 'Pen', price: 5 });
expect(cart.totalPrice()).toBe(15);
});
// 实现
class ShoppingCart {
constructor() {
this.items = [];
}
addItem(item) {
this.items.push(item);
}
totalPrice() {
return this.items.reduce((sum, item) => sum + item.price, 0);
}
}
```
### TypeScript (Jest)
```typescript
// 测试
test('should calculate total price', () => {
const cart = new ShoppingCart();
cart.addItem({ name: 'Book', price: 10 });
cart.addItem({ name: 'Pen', price: 5 });
expect(cart.totalPrice()).toBe(15);
});
// 实现
interface Item {
name: string;
price: number;
}
class ShoppingCart {
private items: Item[] = [];
addItem(item: Item): void {
this.items.push(item);
}
totalPrice(): number {
return this.items.reduce((sum, item) => sum + item.price, 0);
}
}
```
---
## 常见问题
### Q1: 是否需要 100% 测试覆盖率?
**A**: 不一定。80-90% 是合理目标。以下情况可以例外:
- UI 组件(优先用 E2E 测试)
- 简单的 getter/setter
- 第三方库的封装
### Q2: 如何测试私有方法?
**A**: 不要直接测试私有方法。应该通过公共接口测试其行为。如果私有方法太复杂,考虑提取到独立的类。
### Q3: TDD 会降低开发速度吗?
**A**: 短期可能稍慢,但长期来看:
- 减少调试时间
- 减少回归 Bug
- 提高代码可维护性
- **整体效率提升 30-50%**
### Q4: 什么时候不适合 TDD?
**A**:
- 探索性原型(POC)
- UI 设计探索
- 紧急热修复(但仍应事后补充测试)
---
## 验证清单
完成 TDD 开发后,检查:
- [ ] 所有测试通过
- [ ] 测试覆盖率 ≥ 80%
- [ ] 每个测试用例独立且可重复
- [ ] 测试命名清晰(Should_ExpectedBehavior_When_StateUnderTest)
- [ ] 遵循 AAA 模式
- [ ] 无伪测试(所有测试都有断言)
- [ ] 边界条件已测试
- [ ] 异常情况已测试
---
## 相关参考
- [TDD 最佳实践](../references/tdd-best-practices.md)
- [测试覆盖率配置](../config.yaml#tdd)
No comments yet. Be the first to comment!