Skip to content
Testing
Skill

/pytest-patterns

Python testing skill using pytest, covering fixtures, parametrize, markers, conftest, plugins, mocking, and advanced testing patterns.

From plugin
qaskills
24413 skills

Claude Code

$ npx -y skills add PramodDutta/qaskills --skill pytest-patterns --agent claude-code

SKILL.md

pytest-patterns.SKILL.md
name: pytest-patterns
description: Python testing skill using pytest, covering fixtures, parametrize, markers, conftest, plugins, mocking, and advanced testing patterns.
license: MIT
metadata:
  author: thetestingacademy
  version: 1.0.0
  source: https://qaskills.sh/skills/thetestingacademy/pytest-patterns

Pytest Patterns Skill

You are an expert Python developer specializing in testing with pytest. When the user asks you to write, review, or debug pytest tests, follow these detailed instructions.

Core Principles

1. **Convention over configuration** -- pytest discovers tests automatically by naming conventions. 2. **Fixtures for setup** -- Use fixtures instead of setUp/tearDown methods. 3. **Parametrize for coverage** -- Use `@pytest.mark.parametrize` for data-driven tests. 4. **Descriptive test names** -- Function names should describe the expected behavior. 5. **Minimal test scope** -- Each test verifies one behavior.

Project Structure

project/
  src/
    myapp/
      __init__.py
      services/
        user_service.py
        order_service.py
      models/
        user.py
      utils/
        validators.py
  tests/
    __init__.py
    conftest.py
    unit/
      __init__.py
      test_user_service.py
      test_validators.py
    integration/
      __init__.py
      conftest.py
      test_user_api.py
    fixtures/
      user_fixtures.py
  pyproject.toml
  pytest.ini

Configuration

# pytest.ini
[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = -v --tb=short --strict-markers
markers =
    slow: marks tests as slow (deselect with '-m "not slow"')
    integration: marks integration tests
    smoke: marks smoke tests
    unit: marks unit tests
# pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v --tb=short --strict-markers --cov=src --cov-report=term-missing"
markers = [
    "slow: marks tests as slow",
    "integration: marks integration tests",
    "smoke: marks smoke tests",
]

[tool.coverage.run]
source = ["src"]
omit = ["tests/*", "*/__init__.py"]

[tool.coverage.report]
fail_under = 80
show_missing = true

Fixtures

Basic Fixtures

# conftest.py
import pytest
from myapp.services.user_service import UserService
from myapp.models.user import User


@pytest.fixture
def sample_user():
    """Create a sample user for testing."""
    return User(
        id="user-123",
        email="test@example.com",
        name="Test User",
        role="user",
    )


@pytest.fixture
def admin_user():
    """Create an admin user for testing."""
    return User(
        id="admin-123",
        email="admin@example.com",
        name="Admin User",
        role="admin",
    )


@pytest.fixture
def user_service(mock_user_repo, mock_email_service):
    """Create UserService with mocked dependencies."""
    return UserService(
        user_repo=mock_user_repo,
        email_service=mock_email_service,
    )

Fixture Scopes

@pytest.fixture(scope="session")
def database_connection():
    """Create a database connection once for the entire test session."""
    conn = create_connection("test_db")
    yield conn
    conn.close()


@pytest.fixture(scope="module")
def test_data(database_connection):
    """Seed test data once per module."""
    seed_test_data(database_connection)
    yield
    cleanup_test_data(database_connection)


@pytest.fixture(scope="function")  # default scope
def fresh_user():
    """Create a fresh user for each test function."""
    return create_user(email=f"test-{uuid4()}@example.com")


@pytest.fixture(scope="class")
def shared_resource():
    """Share a resource across all methods in a test class."""
    resource = create_expensive_resource()
    yield resource
    resource.cleanup()

Fixture Factories

@pytest.fixture
def make_user():
    """Factory fixture that creates users with custom attributes."""
    created_users = []

    def _make_user(
        email: str = None,
        name: str = "Test User",
        role: str = "user",
    ) -> User:
        user = User(
            id=str(uuid4()),
            email=email or f"test-{uuid4()}@example.com",
            name=name,
            role=role,
        )
        created_users.append(user)
        return user

    yield _make_user

    # Cleanup
    for user in created_users:
        try:
            delete_user(user.id)
        except Exception:
            pass


# Usage in tests
def test_admin_permissions(make_user):
    admin = make_user(role="admin")
    viewer = make_user(role="viewer")
    assert admin.can_delete_users()
    assert not viewer.can_delete_users()

Yield Fixtures (Setup/Teardown)

@pytest.fixture
def temp_file(tmp_path):
    """Create a temporary file and clean up after test."""
    file_path = tmp_path / "test_data.json"
    file_path.write_text('{"key": "value"}')
    yield file_path
    # Teardown happens automatically (tmp_path handles cleanup)


@pytest.fixture
def mock_server():
    """Start a mock HTTP server for testing."""
    server = MockServer(port=8089)
    server.start()
    yield server
    server.stop()


@pytest.fixture
def db_transaction(database_connection):
    """Wrap each test in a database transaction that rolls back."""
    transaction = database_connection.begin()
    yield database_connection
    transaction.rollback()

Parametrize

Basic Parametrize

@pytest.mark.parametrize("email,expected", [
    ("user@example.com", True),
    ("first.last@domain.co.uk", True),
    ("user+tag@example.com", True),
    ("", False),
    ("not-an-email", False),
    ("@missing-local.com", False),
    ("missing-at.com", False),
])
def test_is_valid_email(email, expected):
    assert is_valid_email(email) == expected

Multiple Parameters

@pytest.mark.parametrize("a,b,expected", [
    (1, 2, 3),
    (0, 0, 0),
    (-1, 1, 0),
    (100, 200, 300),
])
def
Read more
@pramoddutta
Ships withqaskills

QA Skills Directory QA Skills is a curated directory of testing-specific skills for AI coding agents (Claude Code, Cursor, Copilot, etc.).

Stars on GitHub

Momentum

stars in total
244
Star history of PramodDutta/qaskills on GitHub, a chart by star-history.com

Other skills on qaskills.

add-seed-skills

add-seed-skills

Use when adding or editing QA skills in seed-skills/ or getting them onto the live qaskills.sh catalog, e.g. "add N new skills",…

publish-seo-batch

publish-seo-batch

Use when publishing SEO blog articles to qaskills.sh, e.g. "publish today's articles", "daily SEO batch", "write 10 articles from…

ship-prod

ship-prod

Use when deploying qaskills.sh to production, verifying whether a deploy landed, or when a push to main did not show up on the…

api-testing-rest

api-testing-rest

Comprehensive RESTful API testing patterns covering HTTP methods, status codes, request/response validation, authentication,…

claude-code-qa

claude-code-qa

The complete QA skill for Claude Code — turn Claude into an expert QA engineer that picks the right test type, writes reliable…

cypress-e2e

cypress-e2e

End-to-end testing skill using Cypress for web applications, covering custom commands, network intercepts, fixtures, cy.session,…

More skills from other plugins.

api-review

api-review

Produce the "Playwright 1.xx API Review" artifact for a range of commits - the public API additions and the breaking or behavior…

@microsoft@microsoftInstall Skill
add-e2e-selectors

add-e2e-selectors

Add reliable @grafana/e2e-selectors to interactive elements and key containers in the Grafana frontend. Use when adding e2e…

grafana1d78k
@grafana@grafanaInstall Skill
promptfoo-evals

promptfoo-evals

Write, refine, run, and QA promptfoo evaluation suites: promptfooconfig.yaml, prompts, providers, vars, tests, assertions,…

@promptfoo@promptfooInstall Skill
deepeval

deepeval

DeepEval evaluation workflow for AI agents and LLM applications. TRIGGER when the user wants to evaluate or improve an AI agent,…

@confident-ai@confident-aiInstall Skill

Questions about pytest-patterns.

Which coding agents can use pytest-patterns?
pytest-patterns installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.