manutej/luxor-claude-marketplace3 files

Pytest Patterns

Python testing with pytest covering fixtures, parametrization, mocking, and test organization for reliable test suites

Specification
Skill ID
manutej/luxor-claude-marketplace/pytest-patterns
Publisher
manutej
Repository
luxor-claude-marketplace
Installs
0
Files
3
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

manutej/luxor-claude-marketplace/pytest-patternsInstalls these files
  • EXAMPLES.md
  • README.md
  • SKILL.md

What this skill tells the agent

Pytest Patterns - Comprehensive Testing Guide

A comprehensive skill for mastering Python testing with pytest. This skill covers everything from basic test structure to advanced patterns including fixtures, parametrization, mocking, test organization, coverage analysis, and CI/CD integration.

When to Use This Skill

Use this skill when:

  • Writing tests for Python applications (web apps, APIs, CLI tools, libraries)
  • Setting up test infrastructure for a new Python project
  • Refactoring existing tests to be more maintainable and efficient
  • Implementing test-driven development (TDD) workflows
  • Creating fixture patterns for database, API, or external service testing
  • Organizing large test suites with hundreds or thousands of tests
  • Debugging failing tests or improving test reliability
  • Setting up continuous integration testing pipelines
  • Measuring and improving code coverage
  • Writing integration, unit, or end-to-end tests
  • Testing async Python code
  • Mocking external dependencies and services

Core Concepts

What is pytest?

pytest is a mature, full-featured Python testing framework that makes it easy to write simple tests, yet scales to support complex functional testing. It provides:

  • Simple syntax: Use plain assert statements instead of special assertion methods
  • Powerful fixtures: Modular, composable test setup and teardown
  • Parametrization: Run the same test with different inputs
  • Plugin ecosystem: Hundreds of plugins for extended functionality
  • Detailed reporting: Clear failure messages and debugging information
  • Test discovery: Automatic test collection following naming conventions

pytest vs unittest

# unittest (traditional)
import unittest

class TestMath(unittest.TestCase):
    def test_addition(self):
        self.assertEqual(2 + 2, 4)

# pytest (simpler)
def test_addition():
    assert 2 + 2 == 4

Test Discovery Rules

pytest automatically discovers tests by following these conventions:

  1. Test files: test_*.py or *_test.py
  2. Test functions: Functions prefixed with test_
  3. Test classes: Classes prefixed with Test (no __init__ method)
  4. Test methods: Methods prefixed with test_ inside Test classes

Fixtures - The Heart of pytest

What are Fixtures?

Fixtures provide a fixed baseline for tests to run reliably and repeatably. They handle setup, provide test data, and perform cleanup.

Basic Fixture Pattern

import pytest

@pytest.fixture
def sample_data():
    """Provides sample data for testing."""
    return {"name": "Alice", "age": 30}

def test_data_access(sample_data):
    assert sample_data["name"] == "Alice"
    assert sample_data["age"] == 30

Fixture Scopes

Fixtures can have different scopes controlling how often they're created:

  • function (default): Created for each test function
  • class: Created once per test class
  • module: Created once per test module
  • package: Created once per test package
  • session: Created once per test session
@pytest.fixture(scope="session")
def database_connection():
    """Database connection created once for entire test session."""
    conn = create_db_connection()
    yield conn
    conn.close()  # Cleanup after all tests

@pytest.fixture(scope="module")
def api_client():
    """API client created once per test module."""
    client = APIClient()
    client.authenticate()
    yield client
    client.logout()

@pytest.fixture  # scope="function" is default
def temp_file():
    """Temporary file created for each test."""
    import tempfile
    f = tempfile.NamedTemporaryFile(mode='w', delete=False)
    yield f.name
    os.unlink(f.name)

Fixture Dependencies

Fixtures can depend on other fixtures, creating a dependency graph:

@pytest.fixture
def database():
    db = Database()
    db.connect()
    yield db
    db.disconnect()

@pytest.fixture
def user_repository(database):
    """Depends on database fixture."""
    return UserRepository(database)

@pytest.fixture
def sample_user(user_repository):
    """Depends on user_repository, which depends on database."""
    user = user_repository.create(name="Test User")
    yield user
    user_repository.delete(user.id)

def test_user_operations(sample_user):
    """Uses sample_user fixture (which uses user_repository and database)."""
    assert sample_user.name == "Test User"

Autouse Fixtures

Fixtures that run automatically without being explicitly requested:

@pytest.fixture(autouse=True)
def reset_database():
    """Runs before every test automatically."""
    clear_database()
    seed_test_data()

@pytest.fixture(autouse=True, scope="session")
def configure_logging():
    """Configure logging once for entire test session."""
    import logging
    logging.basicConfig(level=logging.DEBUG)

Fixture Factories

Fixtures that return functions for creating test data:

@pytest.fixture
def make_user():
    """Factory fixture for creating users."""
    users = []

    def _make_user(name, email=None):
        user = User(name=name, email=email or f"{name}@example.com")
        users.append(user)
        return user

    yield _make_user

    # Cleanup all created users
    for user in users:
        user.delete()

def test_multiple_users(make_user):
    user1 = make_user("Alice")
    user2 = make_user("Bob", email="bob@test.com")
    assert user1.name == "Alice"
    assert user2.email == "bob@test.com"

Parametrization - Testing Multiple Cases

Basic Parametrization

Run the same test with different inputs:

import pytest

@pytest.mark.parametrize("input_value,expected", [
    (2, 4),
    (3, 9),
    (4, 16),
    (5, 25),
])
def test_square(input_value, expected):
    assert input_value ** 2 == expected

Multiple Parameters

@pytest.mark.parametrize("x", [0, 1])
@pytest.mark.parametrize("y", [2, 3])
def test_combinations(x, y):
    """Runs 4 times: (0,2), (0,3), (1,2), (1,3)."""
    assert x < y

Parametrizing with IDs

Make test output more readable:

@pytest.mark.parametrize("test_input,expected", [
    pytest.param("3+5", 8, id="addition"),
    pytest.param("2*4", 8, id="multiplication"),
    pytest.param("10-2", 8, id="subtraction"),
])
def test_eval(test_input, expected):
    assert eval(test_input) == expected

# Output:
# test_eval[addition] PASSED
# test_eval[multiplication] PASSED
# test_eval[subtraction] PASSED

Parametrizing Fixtures

Create fixture instances with different values:

@pytest.fixture(params=["mysql", "postgresql", "sqlite"])
def database_type(request):
    """Test runs three times, once for each database."""
    return request.param

def test_database_connection(database_type):
    conn = connect_to_database(database_type)
    assert conn.is_connected()

Combining Parametrization and Marks

@pytest.mark.parametrize("test_input,expected", [
    ("valid@email.com", True),
    ("invalid-email", False),
    pytest.param("edge@case", True, marks=pytest.mark.xfail),
    pytest.param("slow@test.com", True, marks=pytest.mark.slow),
])
def test_email_validation(test_input, expected):
    assert is_valid_email(test_input) == expected

Indirect Parametrization

Pass parameters through fixtures:

@pytest.fixture
def database(request):
    """Create database based on parameter."""
    db_type = request.param
    db = Database(db_type)
    db.connect()
    yield db
    db.close()

@pytest.mark.parametrize("database", ["mysql", "postgres"], indirect=True)
def test_database_operations(database):
    """database fixture receives the parameter value."""
    assert database.is_connected()
    database.execute("SELECT 1")

Mocking and Monkeypatching

Using pytest's monkeypatch

The monkeypatch fixture provides safe patching that's automatically undone:

def test_get_user_env(monkeypatch):

Use this skill in a project

Open any RiverX project, search the Skills panel for manutej/luxor-claude-marketplace/pytest-patterns, and install it in one click.

Use this skill