Claude
Skills
Sign in
Back

setup-pytest-fixtures

Included with Lifetime
$97 forever

Creates pytest fixtures following project patterns including factory fixtures, async fixtures, and multi-layer organization. Use when setting up test fixtures, creating test data, organizing test utilities, or structuring conftest.py files. Works with Python test files, pytest configuration, and .py test utilities.

Code Reviewscripts

What this skill does


# Setup Pytest Fixtures

## Purpose

Creates pytest fixtures following project-watch-mcp patterns: factory fixtures for customization, async fixtures for async code, centralized organization in tests/utils/, and proper conftest.py hierarchy.

## Quick Start

**Create a basic fixture:**
```python
import pytest

@pytest.fixture
def mock_settings():
    """Create mock settings for testing."""
    settings = MagicMock()
    settings.project = MagicMock()
    settings.project.project_name = "test_project"
    return settings
```

**Create a factory fixture:**
```python
@pytest.fixture
def mock_settings_factory():
    """Factory for custom mock settings."""
    def create_settings(**kwargs):
        settings = MagicMock()
        settings.project = MagicMock()
        settings.project.project_name = kwargs.get("project_name", "test_project")
        return settings
    return create_settings
```

## Instructions

### Step 1: Identify Fixture Type

Determine which fixture pattern to use:

1. **Basic Fixture**: Simple, reusable test data
   - Use for: Constant values, simple mocks
   - Example: `mock_settings_minimal`

2. **Factory Fixture**: Customizable with parameters
   - Use for: Configurable test data, parameterized tests
   - Example: `mock_settings_factory(**kwargs)`

3. **Async Fixture**: For async operations
   - Use for: Async setup/teardown, async resources
   - Example: `@pytest_asyncio.fixture async def...`

4. **Scoped Fixture**: Shared across multiple tests
   - Use for: Expensive setup, session-wide resources
   - Example: `@pytest.fixture(scope="session")`

### Step 2: Choose Organization Strategy

**Centralized Utilities** (Recommended for reusable fixtures):
- Location: `tests/utils/`
- Files: `mock_settings.py`, `mock_services.py`, `mock_drivers.py`, `environmental_helpers.py`
- Import in: `tests/conftest.py` to make available project-wide

**Local Conftest** (For layer-specific fixtures):
- Location: `tests/unit/conftest.py`, `tests/integration/conftest.py`, `tests/e2e/conftest.py`
- Use for: Fixtures specific to that test layer

**Test File** (For test-specific fixtures):
- Location: Same file as tests
- Use for: One-off fixtures not reused elsewhere

### Step 3: Implement Fixture

**For Basic Fixtures:**
```python
import pytest
from unittest.mock import MagicMock

@pytest.fixture
def fixture_name():
    """Clear docstring explaining purpose."""
    # Setup
    obj = MagicMock()
    obj.attribute = "value"

    # Return (no yield for simple fixtures)
    return obj
```

**For Factory Fixtures:**
```python
@pytest.fixture
def fixture_factory():
    """Factory for custom fixture."""
    def create_fixture(**kwargs):
        """Create fixture with custom attributes.

        Args:
            **kwargs: Attributes to customize

        Returns:
            MagicMock: Configured fixture
        """
        obj = MagicMock()
        obj.attribute = kwargs.get("attribute", "default_value")
        return obj
    return create_fixture
```

**For Async Fixtures:**
```python
import pytest_asyncio
from collections.abc import AsyncGenerator

@pytest_asyncio.fixture(scope="function")
async def async_fixture() -> AsyncGenerator[Resource, None]:
    """Async fixture with setup and teardown."""
    # Setup
    resource = await create_resource()

    yield resource

    # Teardown
    await resource.close()
```

**For Scoped Fixtures:**
```python
@pytest.fixture(scope="session")
def session_fixture():
    """Session-scoped fixture created once."""
    # Expensive setup
    resource = expensive_setup()

    yield resource

    # Cleanup after all tests
    resource.cleanup()
```

### Step 4: Add to Conftest Hierarchy

**In `tests/utils/mock_*.py`:**
```python
"""Reusable mock fixtures for [component] components."""
from unittest.mock import MagicMock
import pytest

@pytest.fixture
def fixture_name():
    """Fixture docstring."""
    return MagicMock()
```

**In `tests/conftest.py`:**
```python
# Import to make available project-wide
from tests.utils.mock_settings import (
    fixture_name,
)

__all__ = [
    "fixture_name",
]
```

**In layer-specific conftest:**
```python
# tests/unit/conftest.py
import pytest

@pytest.fixture
def unit_specific_fixture():
    """Fixture only for unit tests."""
    return MagicMock()
```

### Step 5: Use Fixtures in Tests

**Basic usage:**
```python
def test_something(fixture_name):
    """Test using fixture."""
    assert fixture_name.attribute == "value"
```

**Factory usage:**
```python
def test_with_factory(fixture_factory):
    """Test using factory fixture."""
    custom = fixture_factory(attribute="custom_value")
    assert custom.attribute == "custom_value"
```

**Async usage:**
```python
@pytest.mark.asyncio
async def test_async(async_fixture):
    """Test using async fixture."""
    result = await async_fixture.operation()
    assert result == expected
```

**Multiple fixtures:**
```python
def test_with_multiple(fixture_one, fixture_two, fixture_factory):
    """Test using multiple fixtures."""
    custom = fixture_factory(value="test")
    assert fixture_one.works_with(fixture_two, custom)
```

## Examples

### Example 1: Basic Mock Fixture

```python
import pytest
from unittest.mock import MagicMock

@pytest.fixture
def mock_settings_minimal():
    """Create minimal mock settings for basic testing."""
    settings = MagicMock()
    settings.project = MagicMock()
    settings.project.project_name = "test_project"
    settings.neo4j = MagicMock()
    settings.neo4j.database_name = "test_db"
    return settings
```

### Example 2: Factory Fixture

```python
@pytest.fixture
def mock_settings_factory():
    """Factory for custom mock settings."""
    def create_settings(**kwargs):
        settings = MagicMock()
        settings.project = MagicMock()
        settings.project.project_name = kwargs.get("project_name", "test_project")
        settings.neo4j = MagicMock()
        settings.neo4j.database_name = kwargs.get("database_name", "test_db")
        settings.chunking = MagicMock()
        settings.chunking.chunk_size = kwargs.get("chunk_size", 50)
        return settings
    return create_settings
```

### Example 3: Async Fixture with Cleanup

```python
import pytest_asyncio
from collections.abc import AsyncGenerator
from neo4j import AsyncGraphDatabase, AsyncDriver

@pytest_asyncio.fixture(scope="function")
async def real_neo4j_driver() -> AsyncGenerator[AsyncDriver, None]:
    """Create a real Neo4j driver with cleanup."""
    driver = AsyncGraphDatabase.driver(
        "bolt://localhost:7687",
        auth=("neo4j", "password")
    )

    # Setup: Clear test data
    async with driver.session(database="test_db") as session:
        await session.run("MATCH (n {project_name: 'test_project'}) DETACH DELETE n")

    yield driver

    # Teardown: Close driver
    await driver.close()
```

**Script Files:**
- [scripts/generate_fixture.py](./scripts/generate_fixture.py) - Auto-generate pytest fixtures following project patterns
- [scripts/analyze_fixture_usage.py](./scripts/analyze_fixture_usage.py) - Analyze pytest fixture usage and find optimization opportunities
- [scripts/organize_fixtures.py](./scripts/organize_fixtures.py) - Reorganize fixtures by layer and update imports

## Requirements

- pytest installed: `uv pip install pytest`
- pytest-asyncio for async fixtures: `uv pip install pytest-asyncio`
- Understanding of project structure:
  - `tests/utils/` - Centralized reusable fixtures
  - `tests/conftest.py` - Project-wide fixture imports
  - `tests/unit/conftest.py` - Unit test specific fixtures
  - `tests/integration/conftest.py` - Integration test specific fixtures
  - `tests/e2e/conftest.py` - E2E test specific fixtures

## Fixture Best Practices

1. **Naming**: Use descriptive names with `mock_` prefix for mocks
2. **Docstrings**: Always document what the fixture provides
3. **Scope**: Default to `function` scope, use `session`/`module` only when needed
4. **Cleanup**: Use `yield` for fixtures tha

Related in Code Review