Python Testing with pytest

中级 Intermediate 参考型 Reference ⚡ Claude Code 专属 ⚡ Claude Code Optimized
4 min read · 209 lines

Comprehensive Python testing with pytest — TDD, fixtures, mocking, and parametrize

Python Testing with pytest

Source: affaan-m/everything-claude-code Original files: skills/python-testing/SKILL.md, rules/python/testing.md Curated: 2026-02-21

TDD Cycle

# RED: Write failing test
def test_add_numbers():
    assert add(2, 3) == 5

# GREEN: Minimal implementation
def add(a, b):
    return a + b

# REFACTOR: Improve while green

Fixtures

Basic

@pytest.fixture
def sample_data():
    return {"name": "Alice", "age": 30}

Setup/Teardown

@pytest.fixture
def database():
    db = Database(":memory:")
    db.create_tables()
    yield db
    db.close()

Scopes

@pytest.fixture(scope="function")  # Default -- per test
@pytest.fixture(scope="module")    # Once per module
@pytest.fixture(scope="session")   # Once per session

Parametrized Fixtures

@pytest.fixture(params=["sqlite", "postgresql", "mysql"])
def db(request):
    return Database(request.param)

Shared via conftest.py

# tests/conftest.py
@pytest.fixture
def client():
    app = create_app(testing=True)
    with app.test_client() as client:
        yield client

Parametrization

@pytest.mark.parametrize("input,expected", [
    ("hello", "HELLO"),
    ("world", "WORLD"),
], ids=["lowercase", "another"])
def test_uppercase(input, expected):
    assert input.upper() == expected

@pytest.mark.parametrize("a,b,expected", [
    (2, 3, 5), (0, 0, 0), (-1, 1, 0),
])
def test_add(a, b, expected):
    assert add(a, b) == expected

Mocking

from unittest.mock import patch, Mock

@patch("mypackage.external_api_call")
def test_with_mock(api_mock):
    api_mock.return_value = {"status": "success"}
    result = my_function()
    api_mock.assert_called_once()

@patch("mypackage.api_call")
def test_error(api_mock):
    api_mock.side_effect = ConnectionError("fail")
    with pytest.raises(ConnectionError):
        api_call()

@patch("mypackage.DBConnection", autospec=True)
def test_autospec(db_mock):
    # Catches API misuse
    db_mock.return_value.query("SELECT 1")

Async Testing

@pytest.mark.asyncio
async def test_async_function():
    result = await async_add(2, 3)
    assert result == 5

@pytest.mark.asyncio
@patch("mypackage.async_api_call")
async def test_async_mock(api_mock):
    api_mock.return_value = {"status": "ok"}
    result = await my_async_function()
    api_mock.assert_awaited_once()

Markers

@pytest.mark.slow
@pytest.mark.integration
@pytest.mark.unit
pytest -m "not slow"          # Skip slow
pytest -m integration         # Only integration
pytest -m "unit and not slow" # Unit, excluding slow

Test Organization

tests/
├── conftest.py
├── unit/
│   ├── test_models.py
│   └── test_utils.py
├── integration/
│   ├── test_api.py
│   └── test_database.py
└── e2e/
    └── test_user_flow.py

Configuration

# pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = ["--strict-markers", "--cov=mypackage", "--cov-report=term-missing"]
markers = ["slow", "integration", "unit"]

Essential Commands

pytest                          # All tests
pytest tests/test_utils.py      # Specific file
pytest -v                       # Verbose
pytest --cov=mypackage          # With coverage
pytest -m "not slow"            # Skip slow
pytest -x                       # Stop on first failure
pytest --lf                     # Last failed
pytest -k "test_user"           # Pattern match
pytest --pdb                    # Debug on failure

Best Practices

DO: Follow TDD, test one thing per test, use descriptive names, use fixtures, mock externals, test edge cases, aim for 80%+

DON'T: Test implementation details, use complex conditionals in tests, ignore failures, test third-party code, share state between tests, catch exceptions in tests

相关技能 Related Skills