mcpField
P
python-testing-patterns

python-testing-patterns

github.com/full-stack-skills测试与质量3101 次安装4 周前更新
代码仓库

关于 python-testing-patterns

python-testing-patterns 面向设计或维护测试套件的 Python 开发者,介绍基于 pytest 的夹具、模拟、测试结构、覆盖率和测试流程,并指向配套文档中的更多模式。

主要功能

  1. 01pytest 测试组织
  2. 02夹具与测试隔离
  3. 03模拟外部依赖
  4. 04AAA 测试结构
  5. 05覆盖率报告指导
  6. 06TDD 与集成测试指导

适用场景

  1. 01搭建 Python 测试套件
  2. 02测试重试与时间相关行为
  3. 03检查 API 或服务集成
  4. 04排查失败的测试

安装

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

$npx skills add https://github.com/full-stack-skills/python-skills --skill python-testing-patterns

安装后在 Claude Code 中输入/python-testing-patterns确认已加载。

包含的文件

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

LICENSE.txt
agents/
openai.yaml
examples/
advanced-usage.md
basic-usage.md
integration.md
references/
advanced-patterns.md
details.md

详细介绍

来源声明:本技能内容源自 wshobson/agents (MIT License,Copyright (c) 2024 Seth Hobson;完整许可文本见同目录 LICENSE.txt)。 使用、修改与再分发须遵守该 MIT 许可条款并保留版权声明。

中文描述

本技能提供全栈开发相关的最佳实践和模式指导。


name: python-testing-patterns description: 使用 pytest、fixtures、mocking 和测试驱动开发实施全面的测试策略。适用于编写 Python 测试、搭建测试套件或实施测试最佳实践。

Python 测试模式

全面介绍如何在 Python 中使用 pytest、fixtures、mocking、参数化和测试驱动开发实践来实施稳健的测试策略。

何时使用此技能

  • 为 Python 代码编写单元测试
  • 搭建测试套件和测试基础设施
  • 实施测试驱动开发(TDD)
  • 为 API 和服务创建集成测试
  • 模拟外部依赖和服务
  • 测试异步代码和并发操作
  • 在 CI/CD 中设置持续测试
  • 实施基于属性的测试
  • 测试数据库操作
  • 调试失败的测试

核心概念

1. 测试类型
  • 单元测试:单独测试函数/类
  • 集成测试:测试组件之间的交互
  • 功能测试:端到端测试完整功能
  • 性能测试:衡量速度和资源使用情况
2. 测试结构(AAA 模式)
  • Arrange(准备):设置测试数据和前置条件
  • Act(执行):执行待测代码
  • Assert(断言):验证结果
3. 测试覆盖率
  • 衡量测试实际执行了哪些代码
  • 找出未经测试的代码路径
  • 追求有意义的覆盖率,而非单纯追求高百分比
4. 测试隔离
  • 测试应相互独立
  • 测试之间不应共享状态
  • 每项测试都应自行清理

快速开始

# test_example.py
def add(a, b):
    return a + b

def test_add():
    """Basic test example."""
    result = add(2, 3)
    assert result == 5

def test_add_negative():
    """Test with negative numbers."""
    assert add(-1, 1) == 0

# Run with: pytest test_example.py

详细模式和实用示例

详细的模式文档位于 references/details.md。如果上文的导航层级不足以满足需求,请阅读该文件。

测试最佳实践

测试组织
# tests/
#   __init__.py
#   conftest.py           # Shared fixtures
#   test_unit/            # Unit tests
#     test_models.py
#     test_utils.py
#   test_integration/     # Integration tests
#     test_api.py
#     test_database.py
#   test_e2e/            # End-to-end tests
#     test_workflows.py
测试命名约定

常见模式:test_<unit>_<scenario>_<expected_outcome>。可根据团队偏好进行调整。

# Pattern: test_<unit>_<scenario>_<expected>
def test_create_user_with_valid_data_returns_user():
    ...

def test_create_user_with_duplicate_email_raises_conflict():
    ...

def test_get_user_with_unknown_id_returns_none():
    ...

# Good test names - clear and descriptive
def test_user_creation_with_valid_data():
    """Clear name describes what is being tested."""
    pass

def test_login_fails_with_invalid_password():
    """Name describes expected behavior."""
    pass

def test_api_returns_404_for_missing_resource():
    """Specific about inputs and expected outcomes."""
    pass

# Bad test names - avoid these
def test_1():  # Not descriptive
    pass

def test_user():  # Too vague
    pass

def test_function():  # Doesn't explain what's tested
    pass
测试重试行为

使用模拟副作用验证重试逻辑是否正常工作。

from unittest.mock import Mock

def test_retries_on_transient_error():
    """Test that service retries on transient failures."""
    client = Mock()
    # Fail twice, then succeed
    client.request.side_effect = [
        ConnectionError("Failed"),
        ConnectionError("Failed"),
        {"status": "ok"},
    ]

    service = ServiceWithRetry(client, max_retries=3)
    result = service.fetch()

    assert result == {"status": "ok"}
    assert client.request.call_count == 3

def test_gives_up_after_max_retries():
    """Test that service stops retrying after max attempts."""
    client = Mock()
    client.request.side_effect = ConnectionError("Failed")

    service = ServiceWithRetry(client, max_retries=3)

    with pytest.raises(ConnectionError):
        service.fetch()

    assert client.request.call_count == 3

def test_does_not_retry_on_permanent_error():
    """Test that permanent errors are not retried."""
    client = Mock()
    client.request.side_effect = ValueError("Invalid input")

    service = ServiceWithRetry(client, max_retries=3)

    with pytest.raises(ValueError):
        service.fetch()

    # Only called once - no retry for ValueError
    assert client.request.call_count == 1
使用 Freezegun 模拟时间

使用 freezegun 控制测试中的时间,从而获得可预测的时间相关行为。

from freezegun import freeze_time
from datetime import datetime, timedelta

@freeze_time("2026-01-15 10:00:00")
def test_token_expiry():
    """Test token expires at correct time."""
    token = create_token(expires_in_seconds=3600)
    assert token.expires_at == datetime(2026, 1, 15, 11, 0, 0)

@freeze_time("2026-01-15 10:00:00")
def test_is_expired_returns_false_before_expiry():
    """Test token is not expired when within validity period."""
    token = create_token(expires_in_seconds=3600)
    assert not token.is_expired()

@freeze_time("2026-01-15 12:00:00")
def test_is_expired_returns_true_after_expiry():
    """Test token is expired after validity period."""
    token = Token(expires_at=datetime(2026, 1, 15, 11, 30, 0))
    assert token.is_expired()

def test_with_time_travel():
    """Test behavior across time using freeze_time context."""
    with freeze_time("2026-01-01") as frozen_time:
        item = create_item()
        assert item.created_at == datetime(2026, 1, 1)

        # Move forward in time
        frozen_time.move_to("2026-01-15")
        assert item.age_days == 14
测试标记
# test_markers.py
import pytest

@pytest.mark.slow
def test_slow_operation():
    """Mark slow tests."""
    import time
    time.sleep(2)


@pytest.mark.integration
def test_database_integration():
    """Mark integration tests."""
    pass


@pytest.mark.skip(reason="Feature not implemented yet")
def test_future_feature():
    """Skip tests temporarily."""
    pass


@pytest.mark.skipif(os.name == "nt", reason="Unix only test")
def test_unix_specific():
    """Conditional skip."""
    pass


@pytest.mark.xfail(reason="Known bug #123")
def test_known_bug():
    """Mark expected failures."""
    assert False


# Run with:
# pytest -m slow          # Run only slow tests
# pytest -m "not slow"    # Skip slow tests
# pytest -m integration   # Run integration tests
覆盖率报告
# Install coverage
pip install pytest-cov

# Run tests with coverage
pytest --cov=myapp tests/

# Generate HTML report
pytest --cov=myapp --cov-report=html tests/

# Fail if coverage below threshold
pytest --cov=myapp --cov-fail-under=80 tests/

# Show missing lines
pytest --cov=myapp --cov-report=term-missing tests/

有关高级模式(异步测试、monkeypatch、基于属性的测试、数据库测试、CI/CD 集成和配置),请参阅 references/advanced-patterns.md

能力边界

✅ 擅长处理
  • 全栈开发相关的最佳实践
  • 代码架构设计指导
  • 性能优化建议
⚠️ 需要素材
  • 具体的项目上下文
  • 技术栈信息
❌ 超出范围
  • 非技术领域的业务决策
  • 具体的代码实现(仅提供指导)

注意事项

1. 过度设计

避免在简单场景中使用复杂的设计模式

2. 忽视性能

不要忽视性能优化,特别是在生产环境中

3. 安全考虑

始终考虑安全性和数据保护

4. 测试覆盖

确保有足够的测试覆盖,特别是边界情况

5. 文档维护

保持文档与代码同步更新

评论

支持 Markdown