| name | python-testing-patterns |
| description | Python testing patterns and best practices using pytest, mocking, and property-based testing. Use when writing unit tests, integration tests, or implementing test-driven development in Python projects. |
Python Testing Patterns
Comprehensive guide to implementing robust testing strategies in Python using pytest, fixtures, mocking, parameterization, and property-based testing.
When to Use This Skill
- Writing unit tests for Python functions and classes
- Setting up comprehensive test suites and infrastructure
- Implementing test-driven development (TDD) workflows
- Creating integration tests for APIs, databases, and services
- Mocking external dependencies and third-party services
- Testing async code and concurrent operations
- Implementing property-based testing with Hypothesis
- Setting up CI/CD test automation
- Debugging failing tests and improving test coverage
Core Concepts
Test Discovery: Files matching test_*.py or *_test.py, functions starting with test_
Fixtures: Reusable test resources with setup and teardown
- Scopes:
function(default),class,module,session - Composition: Build complex fixtures from simple ones
- Share via
conftest.pyfor project-wide availability
Assertions: Use assert statements, pytest.raises() for exceptions
Organization: Separate unit/, integration/, e2e/ directories
Quick Reference
Load detailed references for specific topics:
| Task | Reference File |
|---|---|
| Pytest basics, test structure, AAA pattern | skills/python-testing-patterns/references/pytest-fundamentals.md |
| Fixtures, scopes, setup/teardown, conftest.py | skills/python-testing-patterns/references/fixtures.md |
| Parametrization, multiple test cases | skills/python-testing-patterns/references/parametrized-tests.md |
| Mocking, patching, unittest.mock, pytest-mock | skills/python-testing-patterns/references/mocking.md |
| Async tests, pytest-asyncio, event loops | skills/python-testing-patterns/references/async-testing.md |
| Property-based testing, Hypothesis, strategies | skills/python-testing-patterns/references/property-based-testing.md |
| Monkeypatch, environment variables, attributes | skills/python-testing-patterns/references/monkeypatch.md |
| Test structure, markers, conftest.py patterns | skills/python-testing-patterns/references/test-organization.md |
| Coverage measurement, reports, thresholds | skills/python-testing-patterns/references/coverage.md |
| Database, API, Redis, message queue testing | skills/python-testing-patterns/references/integration-testing.md |
| Best practices, test quality, fixture design | skills/python-testing-patterns/references/best-practices.md |
Workflow
1. Basic Test Setup
# test_example.py
import pytest
def test_something():
"""Descriptive test name."""
# Arrange
expected = 5
# Act
result = 2 + 3
# Assert
assert result == expected
Run tests:
pytest # Run all tests
pytest -v # Verbose output
pytest tests/unit/ # Specific directory
pytest -k "test_user" # Match pattern
pytest -m unit # Run marked tests
2. Using Fixtures
@pytest.fixture
def sample_data():
"""Provide test data."""
data = {"key": "value"}
yield data
# Cleanup if needed
def test_with_fixture(sample_data):
assert sample_data["key"] == "value"
3. Parametrized Tests
@pytest.mark.parametrize("input,expected", [
(2, 4),
(3, 9),
(4, 16),
])
def test_square(input, expected):
assert input ** 2 == expected
4. Mocking External Dependencies
from unittest.mock import patch
@patch("module.external_api_call")
def test_with_mock(mock_api):
mock_api.return_value = {"status": "ok"}
result = my_function()
assert result["status"] == "ok"
mock_api.assert_called_once()
5. Coverage Measurement
pytest --cov=src --cov-report=term-missing
pytest --cov=src --cov-report=html
pytest --cov=src --cov-fail-under=80
6. Test Configuration
pytest.ini:
[pytest]
testpaths = tests
python_files = test_*.py
addopts = -v --strict-markers --cov=src
markers =
unit: Unit tests
integration: Integration tests
slow: Slow tests
Common Patterns
Exception testing:
with pytest.raises(ValueError, match="error message"):
function_that_raises()
Async testing:
@pytest.mark.asyncio
async def test_async_function():
result = await async_operation()
assert result is not None
Temporary files:
def test_file_operation(tmp_path):
test_file = tmp_path / "test.txt"
test_file.write_text("content")
assert test_file.read_text() == "content"
Markers for test selection:
@pytest.mark.slow
@pytest.mark.integration
def test_database_operation():
pass
Common Mistakes
Not using fixtures: Repeating setup code across tests
- Solution: Create fixtures in conftest.py
Tests depending on order: Global state pollution
- Solution: Ensure test independence with proper fixtures
Over-mocking: Mocking internal implementation
- Solution: Mock only external boundaries (APIs, databases)
Missing edge cases: Only testing happy path
- Solution: Test boundary conditions, errors, and invalid inputs
Slow tests: Running full integration tests frequently
- Solution: Separate unit/integration, use markers, optimize fixtures
Ignoring coverage gaps: Not measuring test coverage
- Solution: Use pytest-cov and track metrics
Poor test names: Generic names like
test_1()- Solution: Use descriptive names:
test_<behavior>_<condition>_<expected>
- Solution: Use descriptive names:
No cleanup: Resources not released
- Solution: Use fixtures with proper teardown (yield pattern)
Resources
- pytest: https://docs.pytest.org/
- unittest.mock: https://docs.python.org/3/library/unittest.mock.html
- pytest-asyncio: Testing async code
- pytest-cov: Coverage reporting
- pytest-mock: pytest wrapper for mock
- Hypothesis: https://hypothesis.readthedocs.io/
- pytest-xdist: Parallel test execution
- testcontainers: Docker containers for testing