Files
prompts/docs/skills/pytest-scaffolding/references/naming-and-organization.md
T
2026-06-20 20:02:03 -05:00

118 lines
4.7 KiB
Markdown

# Pytest Naming Conventions and Test Organization
!!! info "Primary sources"
- [Good integration practices](https://docs.pytest.org/en/stable/explanation/goodpractices.html)
- [Changing standard (Python) test discovery](https://docs.pytest.org/en/stable/example/pythoncollection.html)
- [How to use fixtures](https://docs.pytest.org/en/stable/how-to/fixtures.html)
- [How to parametrize fixtures and test functions](https://docs.pytest.org/en/stable/how-to/parametrize.html)
- [Marker examples](https://docs.pytest.org/en/stable/example/markers.html)
## Agent Quick Path
Use this when creating or reorganizing test modules so naming and hierarchy stay predictable.
1. Mirror the product domain structure in `tests/` so ownership is obvious.
2. Encode broad context in module and class names (`test_*.py`, `Test*`).
3. Keep leaf test names short and behavior-focused (`test_*`).
4. Use `class Test<Subject>:` only for grouping related scenarios.
5. Place fixtures in the nearest `conftest.py` needed by scope.
6. Separate expensive tests with markers first, directories second.
## Naming Conventions
### File and directory naming
- Use lowercase snake_case for test file names: `test_user_service.py`.
- Keep directories domain-oriented and stable over time: `tests/orders/`, `tests/billing/`.
- Prefer descriptive test names over internal ticket numbers or implementation details.
### Test function naming
- Prefer hierarchical naming: put broad context in folder/module/class, and keep the function name focused on the final assertion.
- Keep pytest discovery prefixes intact:
- modules start with `test_`
- classes start with `Test`
- functions start with `test_`
- Start with user-visible behavior or contract, not private helper names.
Recommended pattern:
- module: `test_<subject>.py`
- class: `Test<Operation>` or `Test<Scenario>`
- function: `test_<expected_outcome>`
Examples:
- Flat (still valid): `test_create_order_rejects_invalid_currency`
- Class-context: `TestOrder -> TestCreate -> test_rejects_invalid_currency`
- Module-context: `test_order.py -> TestCreate -> test_rejects_invalid_currency`
- Module + class context can similarly shorten:
- `test_token.py -> TestRefresh -> test_rotates_session_id`
- `test_user_list.py -> TestListUsers -> test_returns_empty_for_new_tenant`
### Test class naming
- Use `class Test<SubjectOrScenario>:` for scenario grouping and context reduction.
- Keep class names noun-focused (`TestOrderService`) rather than action-focused.
- Avoid xUnit style setup inheritance when fixtures can express dependencies directly.
## Hierarchy and Organization Patterns
Two patterns work well; choose one and apply it consistently.
### Pattern A: Source-mirror hierarchy (default for product code ownership)
```text
src/
app/
orders/service.py
billing/invoice.py
tests/
app/
orders/test_service.py
billing/test_invoice.py
```
Use this when teams own modules by source path and want direct test-to-source mapping.
### Pattern B: Cost-lane hierarchy (default for CI policy clarity)
```text
tests/
unit/
orders/test_service.py
integration/
api/test_orders.py
persistence/test_order_repository.py
smoke/
test_health.py
```
Use this when CI gating is based on cost lanes and marker filtering.
### Hybrid rule (recommended)
- Keep a source-mirror tree for local ownership.
- Add markers (`unit`, `integration`, `smoke`, `external`) for runtime policy.
- Avoid duplicating both trees unless the repository already requires it.
## Fixture Placement Strategy
- Put universal lightweight fixtures in `tests/conftest.py`.
- Put domain fixtures in subtree `conftest.py` files close to where they are used.
- Keep fixtures composable and explicit; avoid large fixture "god objects".
- Use `yield` fixtures for teardown so cleanup is always paired with setup.
## Parametrize and ID Naming
- Use `pytest.mark.parametrize` for behavior matrices instead of copy/paste tests.
- Provide explicit `ids=` labels when case names are not obvious.
- Keep IDs business-meaningful (`"expired-token"`, `"zero-balance"`) so failures are readable.
## Collection and Structure Checks
Use these checks after introducing new test files or renaming modules:
- `uv run pytest --collect-only -q`
- `uv run pytest -m unit -q`
- `uv run pytest -m "not external" -q`
If collection surprises appear, verify file names, marker registration, and directory placement first.
## Common Anti-Patterns
- Mixed naming styles (`testFoo.py`, `test_foo.py`, `foo_test.py`) in one repository.
- Deep fixture chains that hide setup behavior.
- Test names that encode implementation details instead of behavior.
- Moving slow tests into `unit` directories without marker updates.
- Sharing mutable module-level state across tests.