AgentSkillsCN

Testing with Pytest

Claude 使用 pytest 编写全面的 Python 测试。当编写单元测试、创建 fixture、参数化测试、模拟依赖、测试异步代码,或设置 CI 测试流水线时,可使用此技能。

SKILL.md
--- frontmatter
name: Testing with Pytest
description: Claude writes comprehensive Python tests using pytest. Use when writing unit tests, creating fixtures, parametrizing tests, mocking dependencies, testing async code, or setting up CI test pipelines.

Testing with Pytest

Quick Start

ini
# pytest.ini
[pytest]
testpaths = tests
python_files = test_*.py
addopts = -v --strict-markers --cov=src --cov-report=term-missing --cov-fail-under=80
markers =
    slow: marks tests as slow
    integration: marks tests as integration tests
asyncio_mode = auto
python
# tests/conftest.py
import pytest
from sqlalchemy.orm import Session

@pytest.fixture
def db_session(engine) -> Session:
    """Create database session with automatic rollback."""
    connection = engine.connect()
    transaction = connection.begin()
    session = sessionmaker(bind=connection)()
    yield session
    session.close()
    transaction.rollback()
    connection.close()

Features

FeatureDescriptionReference
FixturesReusable test setup with dependency injectionFixtures Guide
ParametrizationTest multiple inputs with @pytest.mark.parametrizeParametrize
Mockingunittest.mock integration for patchingpytest-mock
Async TestingNative async/await support with pytest-asynciopytest-asyncio
MarkersCategorize and filter testsMarkers
CoverageCode coverage with pytest-covpytest-cov

Common Patterns

Parametrized Validation Tests

python
import pytest
from src.validators import validate_email, ValidationError

class TestEmailValidation:
    @pytest.mark.parametrize("email", [
        "user@example.com",
        "user.name@example.com",
        "user+tag@example.co.uk",
    ])
    def test_valid_emails(self, email: str):
        assert validate_email(email) is True

    @pytest.mark.parametrize("email,error", [
        ("", "Email is required"),
        ("invalid", "Invalid email format"),
        ("@example.com", "Invalid email format"),
    ])
    def test_invalid_emails(self, email: str, error: str):
        with pytest.raises(ValidationError, match=error):
            validate_email(email)

Factory Fixtures

python
@pytest.fixture
def user_factory(db_session):
    """Factory for creating test users."""
    def _create_user(email="test@example.com", name="Test User", **kwargs):
        user = User(email=email, name=name, **kwargs)
        db_session.add(user)
        db_session.commit()
        return user
    return _create_user

@pytest.fixture
def test_user(user_factory):
    return user_factory(email="testuser@example.com")

Mocking External Services

python
from unittest.mock import patch, MagicMock

class TestUserService:
    def test_create_user_sends_email(self, user_service, mock_email_service):
        user = user_service.create_user(email="new@example.com", name="New")

        mock_email_service.send_email.assert_called_once_with(
            to="new@example.com",
            template="welcome",
            context={"name": "New"},
        )

    @patch("src.services.user_service.datetime")
    def test_last_login_updated(self, mock_datetime, user_service, test_user):
        from datetime import datetime
        mock_datetime.utcnow.return_value = datetime(2024, 1, 15, 10, 30)

        user_service.authenticate(test_user.email, "password")
        assert test_user.last_login == datetime(2024, 1, 15, 10, 30)

Async API Testing

python
import pytest
from httpx import AsyncClient

@pytest.mark.asyncio
class TestAPIEndpoints:
    async def test_get_users(self, async_client: AsyncClient, test_user):
        response = await async_client.get("/api/users")
        assert response.status_code == 200
        assert len(response.json()["users"]) >= 1

    async def test_create_user(self, async_client: AsyncClient):
        response = await async_client.post("/api/users", json={
            "email": "new@example.com",
            "name": "New User",
            "password": "SecurePass123!",
        })
        assert response.status_code == 201

Best Practices

DoAvoid
Use descriptive test names explaining the scenarioSharing state between tests
Create reusable fixtures for common setupUsing sleep for timing issues
Use parametrize for testing multiple inputsTesting implementation details
Mock external dependenciesWriting tests that depend on order
Group related tests in classesUsing hardcoded file paths
Use factories for test data creationLeaving commented-out test code

References