python-testing-patterns
1
总安装量
1
周安装量
#46639
全站排名
安装命令
npx skills add https://github.com/rory-data/copilot --skill python-testing-patterns
Agent 安装分布
amp
1
opencode
1
kimi-cli
1
codex
1
github-copilot
1
claude-code
1
Skill 文档
Python Testing Patterns
Master pytest fundamentals through essential patterns and practical examples, from basic unit tests to mocking external dependencies.
When to Use This Skill
- Writing unit tests for Python code
- Setting up test suites and test infrastructure
- Implementing test-driven development (TDD)
- Creating integration tests for APIs and services
- Mocking external dependencies and services
- Testing async code and concurrent operations
- Setting up continuous testing in CI/CD
- Implementing property-based testing
- Testing database operations
- Debugging failing tests
Core Concepts
1. Test Types
- Unit Tests: Test individual functions/classes in isolation
- Integration Tests: Test interaction between components
- Functional Tests: Test complete features end-to-end
- Performance Tests: Measure speed and resource usage
2. Test Structure (AAA Pattern)
- Arrange: Set up test data and preconditions
- Act: Execute the code under test
- Assert: Verify the results
3. Test Coverage
- Measure what code is exercised by tests
- Identify untested code paths
- Aim for meaningful coverage, not just high percentages
4. Test Isolation
- Tests should be independent
- No shared state between tests
- Each test should clean up after itself
Quick Start
# 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: uv run pytest test_example.py
Fundamental Patterns
Pattern 1: Basic pytest Tests
# test_calculator.py
import pytest
class Calculator:
"""Simple calculator for testing."""
def add(self, a: float, b: float) -> float:
return a + b
def divide(self, a: float, b: float) -> float:
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
def test_addition():
"""Test addition."""
calc = Calculator()
assert calc.add(2, 3) == 5
assert calc.add(-1, 1) == 0
def test_division_by_zero():
"""Test division by zero raises error."""
calc = Calculator()
with pytest.raises(ValueError, match="Cannot divide by zero"):
calc.divide(5, 0)
Pattern 2: Fixtures for Setup and Teardown
# test_database.py
import pytest
@pytest.fixture
def db():
"""Fixture that provides connected database."""
# Setup
database = {"connected": True}
yield database
# Teardown
database["connected"] = False
def test_database_query(db):
"""Test using fixture."""
assert db["connected"] is True
Pattern 3: Parametrised Tests
# test_validation.py
import pytest
@pytest.mark.parametrize("email,expected", [
("user@example.com", True),
("invalid.email", False),
])
def test_email_validation(email, expected):
"""Test email validation with various inputs."""
assert is_valid(email) == expected
Pattern 4: Mocking with unittest.mock
# test_api_client.py
import pytest
from unittest.mock import Mock, patch
def test_api_call():
"""Test API call with mock."""
mock_response = Mock()
mock_response.json.return_value = {"id": 1}
with patch("requests.get", return_value=mock_response):
result = get_user(1)
assert result["id"] == 1
Pattern 5: Testing Exceptions
# test_exceptions.py
import pytest
def test_exception():
"""Test exception is raised."""
with pytest.raises(ValueError, match="error message"):
divide(10, 0)
Advanced & Specialised Patterns
For additional testing patterns and advanced techniques, see the reference files:
- Logging, Async, Fixtures & more: Patterns 6-12 cover specialised testing scenarios, including logging, async, and data validation
- Test Debugging: Debugging techniques and pytest commands
- Type-Safe Testing: Protocol-based type checking for mocks
- Configuration & Dependencies: Coverage reporting, database testing, CI/CD, pytest configuration
- Testing Anti-Patterns: Common pitfalls to avoid (production pollution, mock testing, etc.)
Testing Best Practices
Test Organisation
# tests/
# __init__.py
# conftest.py # Shared fixtures
# test_unit/ # Unit tests
# test_integration/ # Integration tests
# test_e2e/ # End-to-end tests
Test Naming
# Good test names describe what is tested and expected outcome
def test_user_creation_with_valid_data():
pass
def test_login_fails_with_invalid_password():
pass
# Bad test names
def test_1(): # Not descriptive
pass
def test_user(): # Too vague
pass
Test Markers
import pytest
@pytest.mark.slow
def test_slow_operation():
pass
@pytest.mark.integration
def test_database_integration():
pass
# Run with:
# uv run pytest -m slow # Run only slow tests
# uv run pytest -m "not slow" # Skip slow tests
Best Practices Summary
- Write tests first (TDD) or alongside code
- One assertion per test when possible
- Use descriptive test names that explain behaviour
- Keep tests independent and isolated
- Use fixtures for setup and teardown
- Mock external dependencies appropriately
- Parametrise tests to reduce duplication
- Test edge cases and error conditions
- Measure coverage but focus on quality
- Run tests in CI/CD on every commit
Quick Reference
- pytest documentation: https://docs.pytest.org/
- unittest.mock: https://docs.python.org/3/library/unittest.mock.html
- hypothesis: Property-based testing library
- pytest-asyncio: Testing async code
- pytest-cov: Coverage measurement and reporting
- pytest-mock: Enhanced mocking with spec support
- pydantic: Data validation and schema testing
- pandera: Schema validation for pandas DataFrames