mcpField
T
test-driven-development

test-driven-development

github.com/bobmatnyc测试与质量78395 次安装15 周前更新
代码仓库

关于 test-driven-development

test-driven-development 为开发者和编码 Agent 提供跨编程语言的 TDD 实践指南。内容按红、绿、重构阶段组织,并涵盖测试结构、模拟、覆盖率和避免脆弱测试的方法。

主要功能

  1. 01红—绿—重构工作流
  2. 02Arrange-Act-Assert 和 Given-When-Then 模式
  3. 03测试命名规范与语言示例
  4. 04模拟对象和测试替身指南
  5. 05覆盖率实践与反模式

适用场景

  1. 01先编写测试再开发功能
  2. 02先用失败测试复现问题再修复
  3. 03重构时检查行为是否保持
  4. 04测试 API 契约和业务逻辑

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill test-driven-development

安装后在 Claude Code 中输入/test-driven-development确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

metadata.json
references/
anti-patterns.md
examples.md
integration.md
philosophy.md
workflow.md

详细介绍

测试驱动开发(TDD)

适用于所有编程语言的全面 TDD 模式与实践。此技能可为每个 Agent 减少约 500–800 行重复的测试指导。

适用场景

以下情况请使用 TDD:

  • 实现新功能
  • 修复 Bug(先为 Bug 编写测试)
  • 重构代码(测试可确保行为保持不变)
  • 开发 API(测试契约)
  • 处理复杂业务逻辑

TDD 工作流(红-绿-重构)

1. 红阶段:编写失败的测试
Write a test that:
- Describes the desired behavior
- Fails for the right reason (not due to syntax errors)
- Is focused on a single behavior
2. 绿阶段:让测试通过
Write the minimum code to:
- Pass the test
- Not introduce regressions
- Follow existing patterns
3. 重构阶段:改进代码
While keeping tests green:
- Remove duplication
- Improve naming
- Simplify logic
- Extract functions/classes

测试结构模式

准备-执行-断言(AAA)
// Arrange: Set up test data and conditions
const user = createTestUser({ role: 'admin' });

// Act: Perform the action being tested
const result = await authenticateUser(user);

// Assert: Verify the outcome
expect(result.isAuthenticated).toBe(true);
expect(result.permissions).toContain('admin');
给定-当-那么(BDD 风格)
Given: A user with admin privileges
When: They attempt to access protected resource
Then: Access is granted with appropriate permissions

测试命名约定

模式:test_should_<expected_behavior>_when_<condition>

示例:

  • test_should_return_user_when_id_exists()
  • test_should_raise_error_when_user_not_found()
  • test_should_validate_email_format_when_creating_account()
特定语言的约定

Python(pytest):

def test_should_calculate_total_when_items_added():
    # Arrange
    cart = ShoppingCart()
    cart.add_item(Item("Book", 10.00))
    cart.add_item(Item("Pen", 1.50))

    # Act
    total = cart.calculate_total()

    # Assert
    assert total == 11.50

JavaScript(Jest):

describe('ShoppingCart', () => {
  test('should calculate total when items added', () => {
    const cart = new ShoppingCart();
    cart.addItem({ name: 'Book', price: 10.00 });
    cart.addItem({ name: 'Pen', price: 1.50 });

    const total = cart.calculateTotal();

    expect(total).toBe(11.50);
  });
});

Go:

func TestShouldCalculateTotalWhenItemsAdded(t *testing.T) {
    // Arrange
    cart := NewShoppingCart()
    cart.AddItem(Item{Name: "Book", Price: 10.00})
    cart.AddItem(Item{Name: "Pen", Price: 1.50})

    // Act
    total := cart.CalculateTotal()

    // Assert
    if total != 11.50 {
        t.Errorf("Expected 11.50, got %f", total)
    }
}

测试类型与范围

单元测试
  • 范围: 单个函数/方法
  • 依赖项: 使用 Mock
  • 速度: 快(每项测试 < 10ms)
  • 覆盖率: 覆盖 80% 以上的代码路径
集成测试
  • 范围: 多个组件
  • 依赖项: 真实依赖项或测试替身
  • 速度: 中等(每项测试 < 1s)
  • 覆盖率: 覆盖关键路径与接口
端到端测试
  • 范围: 完整的用户工作流
  • 依赖项: 真实依赖项(在测试环境中)
  • 速度: 慢(数秒至数分钟)
  • 覆盖率: 覆盖核心用户旅程

Mock 与测试替身

适合使用 Mock 的场景
  • 外部 API 和服务
  • 数据库操作(用于单元测试)
  • 文件系统操作
  • 与时间相关的操作
  • 随机数生成
Mock 类型

Stub: 返回预定义数据

def get_user_stub(user_id):
    return User(id=user_id, name="Test User")

Mock: 验证交互

mock_service = Mock()
service.process_payment(payment_data)
mock_service.process_payment.assert_called_once_with(payment_data)

Fake: 可运行的简化实现

class FakeDatabase:
    def __init__(self):
        self.data = {}

    def save(self, key, value):
        self.data[key] = value

    def get(self, key):
        return self.data.get(key)

测试覆盖率指南

目标覆盖率
  • 关键路径: 100%
  • 业务逻辑: 95% 以上
  • 整个项目: 80% 以上
  • UI 组件: 70% 以上
应测试的内容
  • ✅ 业务逻辑和算法
  • ✅ 边缘情况和边界条件
  • ✅ 错误处理和验证
  • ✅ 状态转换
  • ✅ 公共 API 和接口
不应测试的内容
  • ❌ 框架内部实现
  • ❌ 第三方库
  • ❌ 简单的 getter/setter
  • ❌ 生成的代码
  • ❌ 配置文件

测试最佳实践

1. 每项测试只包含一个断言(尽可能做到)
# Good: Focused test
def test_should_validate_email_format():
    assert is_valid_email("[email protected]") is True

# Avoid: Multiple unrelated assertions
def test_validation():
    assert is_valid_email("[email protected]") is True
    assert is_valid_phone("123-456-7890") is True  # Different concept
2. 测试相互独立
# Good: Each test is self-contained
def test_user_creation():
    user = create_user("[email protected]")
    assert user.email == "[email protected]"

# Avoid: Tests depending on execution order
shared_user = None

def test_create_user():
    global shared_user
    shared_user = create_user("[email protected]")

def test_update_user():  # Depends on previous test
    shared_user.name = "Updated"
3. 提供清晰的测试失败信息
# Good: Clear failure message
assert result.status == 200, f"Expected 200, got {result.status}: {result.body}"

# Avoid: Unclear failure
assert result.status == 200
4. 测试数据构造器
# Good: Reusable test data creation
def create_test_user(**overrides):
    defaults = {
        'email': '[email protected]',
        'name': 'Test User',
        'role': 'user'
    }
    return User(**{**defaults, **overrides})

# Usage
admin = create_test_user(role='admin')
guest = create_test_user(email='[email protected]')

应避免的测试反模式

❌ 测试实现细节
# Bad: Tests internal structure
def test_user_storage():
    user = User("[email protected]")
    assert user._internal_cache is not None  # Implementation detail
❌ 脆弱的测试
# Bad: Breaks with harmless changes
assert user.to_json() == '{"name":"John","email":"[email protected]"}'

# Good: Tests behavior, not format
data = json.loads(user.to_json())
assert data['name'] == "John"
assert data['email'] == "[email protected]"
❌ 在单元测试套件中运行缓慢的测试
# Bad: Real HTTP calls in unit tests
def test_api_integration():
    response = requests.get("https://api.example.com/users")  # Slow!
    assert response.status_code == 200
❌ 通过 UI 测试所有内容
# Bad: Testing business logic through UI
def test_calculation():
    browser.click("#input1")
    browser.type("5")
    browser.click("#input2")
    browser.type("3")
    browser.click("#calculate")
    assert browser.find("#result").text == "8"

# Good: Test logic directly
def test_calculation():
    assert calculate(5, 3) == 8

按语言分类的快速参考

Python(pytest)
# Setup/Teardown
@pytest.fixture
def database():
    db = create_test_database()
    yield db
    db.cleanup()

# Parametrized tests
@pytest.mark.parametrize("input,expected", [
    ("[email protected]", True),
    ("invalid-email", False),
])
def test_email_validation(input, expected):
    assert is_valid_email(input) == expected
JavaScript(Jest)
// Setup/Teardown
beforeEach(() => {
  database = createTestDatabase();
});

afterEach(() => {
  database.cleanup();
});

// Async tests
test('should fetch user data', async () => {
  const user = await fetchUser(1);
  expect(user.name).toBe('John');
});
Go
// Table-driven tests
func TestEmailValidation(t *testing.T) {
    tests := []struct {
        input    string
        expected bool
    }{
        {"[email protected]", true},
        {"invalid-email", false},
    }

    for _, tt := range tests {
        result := IsValidEmail(tt.input)
        if result != tt.expected {
            t.Errorf("IsValidEmail(%s) = %v, want %v",
                tt.input, result, tt.expected)
        }
    }
}

TDD 的实际益处

  • 改进设计: 测试推动更好的 API 设计
  • 文档: 测试可作为可执行文档
  • 信心: 重构变得安全
  • 调试: 测试可快速隔离问题
  • 覆盖率: 确保测试覆盖全面
  • 防止回归: 在部署前捕获 Bug

相关技能

使用测试驱动开发时,以下技能可改进你的工作流:

  • systematic-debugging:测试意外失败时采用先调试的方法
  • react:测试 React 组件、钩子和上下文
  • django:测试 Django 模型、视图和表单
  • fastapi-local-dev:测试 FastAPI 端点和依赖注入

[如果这些技能已部署在你的技能包中,可查阅其中的完整文档]

评论

支持 Markdown