testing-with-pytest

doanchienthangdev's avatarfrom doanchienthangdev

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.

⭐0stars🔀0forks📁View on GitHub🕐Updated Jan 8, 2026

When & Why to Use This Skill

This Claude skill automates the creation of robust Python testing suites using the pytest framework. It streamlines the development of unit tests, integration tests, and asynchronous test cases while implementing best practices like dependency mocking, fixture management, and code coverage analysis to ensure high software reliability and maintainable codebases.

Use Cases

  • Unit Testing: Automatically generating comprehensive test cases for Python functions and classes to validate core logic and handle edge cases.
  • Database Testing: Setting up reusable fixtures for database sessions with automatic rollbacks to ensure clean and isolated test environments.
  • API & Async Testing: Testing asynchronous web endpoints and services using pytest-asyncio and httpx for modern, high-performance web applications.
  • Mocking External Services: Isolating code under test by mocking external APIs, third-party services, or system dependencies using unittest.mock and pytest-mock.
  • CI/CD Pipeline Configuration: Setting up pytest.ini and coverage reports (pytest-cov) to enforce quality gates and maintain high test coverage in automated deployment pipelines.
nameTesting with Pytest
descriptionClaude 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

# 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
# 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

Feature Description Reference
Fixtures Reusable test setup with dependency injection Fixtures Guide
Parametrization Test multiple inputs with @pytest.mark.parametrize Parametrize
Mocking unittest.mock integration for patching pytest-mock
Async Testing Native async/await support with pytest-asyncio pytest-asyncio
Markers Categorize and filter tests Markers
Coverage Code coverage with pytest-cov pytest-cov

Common Patterns

Parametrized Validation Tests

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

@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

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

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

Do Avoid
Use descriptive test names explaining the scenario Sharing state between tests
Create reusable fixtures for common setup Using sleep for timing issues
Use parametrize for testing multiple inputs Testing implementation details
Mock external dependencies Writing tests that depend on order
Group related tests in classes Using hardcoded file paths
Use factories for test data creation Leaving commented-out test code

References