TDD Workflow - 测试驱动开发工作流

SkillDev tools

用于需要以测试驱动开发、遵循“红—绿—重构”循环实现功能或修复缺陷的场景。必须先编写失败测试,再实现代码。

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the TDD Workflow - 测试驱动开发工作流 skill

What this skill tells your AI

The instructions your AI receives, as published by huangwb8/skills in skills/alpha/awesome-code/agents/tdd-workflow/SKILL.md and read by ahel’s review.

铁律

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 模式):

# 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)

最简单的可工作代码

  • 只写足够使测试通过的代码
  • 不追求完美,追求通过
  • 硬编码可以接受(第一步)
# 最初版本 - 硬编码也可以
def get_by_id(user_id):
    if user_id == 123:
        return User(id=123, name="Alice")
    return None

步骤 4:运行测试确认通过

# 运行测试
pytest tests/test_user_service.py -v

# 期望输出
✅ should_return_user_when_id_exists PASSED

步骤 5:重构代码(REFACTOR)

在测试保护下优化

  • 消除重复
  • 提取方法
  • 改善命名
  • 优化结构
# 重构后版本
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. 测试隔离

# 好的示例 - 使用 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. 测试边界条件

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. 测试异常情况

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)

# 测试
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)

// 测试
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)

// 测试
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 模式
  • 无伪测试(所有测试都有断言)
  • 边界条件已测试
  • 异常情况已测试

相关参考

约束

公共硬约束

本块由 docs/templates/skill-common-constraints.md 统一维护;每个 SKILL.md## 约束 必须逐字同步本块,不得在副本中改写公共规则。

  • 任务需要落盘时,使用唯一的 ./.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/ 根目录;共享材料放入 shared/,Skill 专属材料放入该 Skill 的 input/output/log/
  • 正式交付物、源代码和正式计划按项目约定保存,不写入任务工作区;未经授权不覆盖、删除、迁移或远程写入。
  • 项目维护变更检查 BAC 可用性并记录需求、AI 产出、工具结果、文件改动和验证摘要;BAC 只做过程审计,不替代署名、责任或合规判断。
  • 不记录 API Key、访问令牌、密码、Cookie、环境/凭据文件、私有 Prompt、身份信息、本地用户名、主机名或不必要的大体积原始数据。
  • 文件路径必须规范化并限制在授权项目范围内;外部 URL、子进程和网络访问遵循最小权限,防止路径遍历、SSRF 和命令注入。
  • Skill 版本唯一记录在自身 config.yaml:skill_info.version;公开 API、协议、目录或配置变更同步文档与 CHANGELOG.md
  • bensz-collect-bugs 是一个 Agent Skill;仅将 Bensz Agent Skill 或 Bensz 基础设施本身的设计缺陷交给它。先脱敏写入 ~/.bensz-skills/bugs/,当前任务不中断,只有用户明确要求才公开上报,禁止直接修改用户已安装的 Skill 源码。

Signals

GitHub stars
48
Forks
7
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
tdd-workflow-huangwb8
Source
github.com/huangwb8/skills