---
paths:
  - "*/src/**/*.py"
  - "*/tests/**/*.py"
---

# Testing Patterns

Test organization, fixtures, and tooling for codeflash packages.

## Class Organization

```python
class TestRegistry:
    """Tests for svcs.Registry."""

    def test_repr_empty(self, registry):
        """
        repr of an empty registry says 0 registered services.
        """
        assert "<svcs.Registry(num_services=0)>" == repr(registry)

    def test_register_factory_and_get(self, registry):
        """
        A factory can be registered and the service retrieved.
        """
        registry.register_factory(int, lambda: 42)

        assert 42 == registry.get(int)
```

One test class per unit under test. Class docstring names the thing. Method name describes the scenario. Docstring describes the expected behavior. Arrange-act-assert with blank line separation when it aids readability.

## Fixture Patterns

```python
# conftest.py

@pytest.fixture(name="registry")
def _registry():
    return svcs.Registry()

@pytest.fixture(name="container")
def _container(registry):
    return svcs.Container(registry)
```

Private function name (leading underscore), public fixture name via parameter. This prevents pytest from confusing the function with the fixture in imports.

## Parametrize

```python
@pytest.mark.parametrize(
    ("input_bytes", "expected"),
    [
        (b"test", "a94a8fe5"),
        (b"test\r", "b444ac06"),
        (b"", "da39a3ee"),
    ],
)
def test_sha1_hexdigest(self, input_bytes, expected):
    """
    SHA1 digest is computed correctly for various inputs.
    """
    assert expected == pem.Certificate(input_bytes).sha1_hexdigest[:8]
```

Tuple of parameter names, list of tuples of values. Docstring describes the invariant, not one specific case.

## Expected Value on the Left

```python
assert "<svcs.Registry(num_services=0)>" == repr(registry)
assert 42 == result
assert ["a", "b"] == sorted(output)
```

Expected value goes on the left side of `==`. This is deliberate and consistent. It reads as "assert that the expected value equals the actual."

## Async Testing

```python
@pytest.mark.asyncio()
class TestAsyncRegistry:
    async def test_async_factory(self, registry):
        """
        Async factories are awaited when getting services.
        """
        async def factory():
            return 42

        registry.register_factory(int, factory)

        assert 42 == await registry.aget(int)
```

## Tooling

See `.claude/rules/uv.md` for uv usage, pyproject.toml structure, and dependency management.

### pre-commit Configuration

```yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml

  - repo: https://github.com/astral-sh/ruff-pre-commit
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

  - repo: https://github.com/econchick/interrogate
    hooks:
      - id: interrogate

  - repo: https://github.com/codespell-project/codespell
    hooks:
      - id: codespell
```
