测试驱动开发Skill test-driven-development

测试驱动开发(TDD)是一种软件开发方法,通过先写测试、再写代码的循环(红绿重构)来提高代码可靠性、优化设计并促进团队协作。关键词包括测试驱动开发、红绿重构、代码测试、软件质量、回归测试、可测试性、敏捷开发。

测试 0 次安装 0 次浏览 更新于 3/18/2026

name: 测试驱动开发 description: 使用红绿重构循环实现测试驱动开发(TDD)工作流程。适用于编写新功能、修复bug或重构现有代码。始终先写失败的测试,然后编写最少代码使其通过,最后重构。确保代码可靠性、防止回归、通过可测试性要求改进设计、通过测试记录预期行为、实现自信重构,并在整个开发过程中保持高代码质量标准。

测试驱动开发(TDD)

何时使用此技能

  • 从头开始编写新功能或功能
  • 修复bug时添加回归测试以防止复发
  • 在测试覆盖下安全地重构现有代码
  • 为未测试的遗留代码添加测试覆盖
  • 在实现前确保代码按预期行为
  • 通过可测试性约束改进代码设计
  • 记录预期行为和边缘情况
  • 构建必须正确的关键业务逻辑
  • 开发具有明确合约的API或库
  • 处理需要高可靠性的项目
  • 实现复杂算法或业务规则
  • 在团队协作中,测试作为文档使用

实现红绿重构循环,以获得可靠、可测试的代码。

TDD循环

1. 红色 - 编写失败的测试

在实现前编写描述期望行为的测试:

def test_user_can_register():
    """测试用户可以使用有效数据成功注册。"""
    result = register_user(email="test@example.com", password="secure123")
    assert result.success is True
    assert result.user.email == "test@example.com"

为什么这有效:

  • 强制清晰思考需求
  • 早期捕获规格问题
  • 提供对实现的即时反馈
  • 创建活文档

2. 绿色 - 编写最少代码以通过

仅实现使测试通过所需的内容:

def register_user(email: str, password: str) -> RegistrationResult:
    """使用电子邮件和密码注册新用户。"""
    user = User(email=email)
    return RegistrationResult(success=True, user=user)

抵制诱惑:

  • 添加未覆盖的功能
  • 过度设计解决方案
  • 过早优化

3. 重构 - 提高代码质量

在测试通过后,改进代码结构:

def register_user(email: str, password: str) -> RegistrationResult:
    """使用电子邮件和密码注册新用户。"""
    validate_email(email)
    validate_password(password)
    
    hashed_password = hash_password(password)
    user = User(email=email, password_hash=hashed_password)
    user.save()
    
    return RegistrationResult(success=True, user=user)

重构目标:

  • 更好命名
  • 移除重复
  • 改进结构
  • 增强可读性

最佳实践

从最简单测试开始

# 好 - 从简单开始
def test_add_returns_sum():
    assert add(2, 3) == 5

# 避免 - 不要从边缘情况开始
def test_add_handles_overflow_with_large_numbers():
    assert add(sys.maxsize, 1) == expected_overflow_behavior

一次测试一件事

# 好 - 单一关注点
def test_user_registration_creates_user():
    result = register_user("test@example.com", "pass123")
    assert result.user is not None

def test_user_registration_hashes_password():
    result = register_user("test@example.com", "pass123")
    assert result.user.password_hash != "pass123"

# 避免 - 多个断言
def test_user_registration():
    result = register_user("test@example.com", "pass123")
    assert result.user is not None
    assert result.user.password_hash != "pass123"
    assert result.user.email == "test@example.com"
    assert result.success is True

使用描述性测试名称

# 好 - 描述行为
def test_invalid_email_returns_validation_error()
def test_duplicate_email_raises_already_exists_error()
def test_successful_registration_sends_welcome_email()

# 避免 - 模糊名称
def test_register()
def test_email()
def test_validation()

遵循三个A模式

def test_user_can_update_profile():
    # 安排 - 设置测试数据
    user = create_test_user(email="test@example.com")
    
    # 行动 - 执行操作
    result = user.update_profile(name="John Doe", bio="Developer")
    
    # 断言 - 验证结果
    assert result.success is True
    assert user.name == "John Doe"
    assert user.bio == "Developer"

常见模式

测试异常

def test_registration_with_invalid_email_raises_error():
    with pytest.raises(ValidationError) as exc:
        register_user(email="invalid", password="pass123")
    
    assert "valid email" in str(exc.value)

测试异步代码

@pytest.mark.asyncio
async def test_async_user_registration():
    result = await register_user_async("test@example.com", "pass123")
    assert result.success is True

使用夹具

@pytest.fixture
def test_user():
    return User(email="test@example.com", name="Test User")

def test_user_can_login(test_user):
    result = login(test_user.email, "correct_password")
    assert result.success is True

模拟外部依赖

def test_user_registration_sends_email(mocker):
    # 模拟电子邮件服务
    mock_send = mocker.patch('services.email.send_welcome_email')
    
    register_user("test@example.com", "pass123")
    
    # 验证电子邮件已发送
    mock_send.assert_called_once_with("test@example.com")

验证清单

在完成TDD实现前:

  • [ ] 测试在实现前编写
  • [ ] 测试最初失败(红色阶段确认)
  • [ ] 添加最少代码以通过测试(绿色阶段)
  • [ ] 重构代码同时保持测试通过
  • [ ] 测试名称清晰描述行为
  • [ ] 每个测试专注于一个行为
  • [ ] 没有未测试的代码路径
  • [ ] 所有测试一致通过
  • [ ] 代码可读且可维护

TDD的好处

  1. 更好设计 - 先写测试导致更模块化、可测试的代码
  2. 信心 - 全面测试套件捕获回归
  3. 文档 - 测试作为活文档
  4. 更快调试 - 失败精确定位问题
  5. 减少bug - 开发期间捕获边缘情况

何时不使用严格TDD

  • 快速原型/概念验证
  • UI布局实验
  • 探索性编码(学习新API)
  • 简单的getter/setter方法

对于这些情况,在实现后但提交前编写测试。

与开发工作流集成

# TDD开发循环
git checkout -b feature/user-registration

# 1. 编写失败的测试
# 2. 运行测试(应该失败)
pytest tests/test_registration.py

# 3. 实现最少代码
# 4. 运行测试(应该通过)
pytest tests/test_registration.py

# 5. 重构
# 6. 运行测试(应该仍通过)
pytest tests/test_registration.py

# 当所有测试通过时提交
git add .
git commit -m "feat: 使用TDD实现用户注册"

参考文献

  • Kent Beck - “测试驱动开发:示例”
  • Martin Fowler - “重构:改进现有代码设计”
  • pytest文档
  • unittest文档