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

测试驱动开发(TDD)是一种软件开发实践,通过先编写测试用例再实现代码来确保代码可靠性和质量。它遵循红绿重构循环,有助于预防回归错误、提高代码设计、文档化行为,并促进团队协作。关键词:测试驱动开发、TDD、软件开发、测试方法、红绿重构、代码测试、回归预防。

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

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

测试驱动开发 (TDD)

何时使用此技能

  • 从头开始编写新功能或功能
  • 用回归测试修复错误以防止再次发生
  • 安全地使用测试覆盖重构现有代码
  • 向未经测试的遗留代码添加测试覆盖
  • 在实现前确保代码行为符合预期
  • 通过可测试性约束改进代码设计
  • 记录预期行为和边界案例
  • 构建必须正确的关键业务逻辑
  • 开发具有明确合同的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. 减少错误 - 边界案例在开发期间捕获

何时不使用严格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: implement user registration with TDD"

参考资料