Files
2026-06-20 20:02:03 -05:00

4.7 KiB

Pytest Naming Conventions and Test Organization

!!! info "Primary sources" - Good integration practices - Changing standard (Python) test discovery - How to use fixtures - How to parametrize fixtures and test functions - Marker examples

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)

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)

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.

  • 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.