Pytest Naming Conventions and Test Organization¶
Primary sources
Agent Quick Path¶
Use this when creating or reorganizing test modules so naming and hierarchy stay predictable.
- Mirror the product domain structure in
tests/so ownership is obvious. - Encode broad context in module and class names (
test_*.py,Test*). - Keep leaf test names short and behavior-focused (
test_*). - Use
class Test<Subject>:only for grouping related scenarios. - Place fixtures in the nearest
conftest.pyneeded by scope. - 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.
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.pyfiles close to where they are used. - Keep fixtures composable and explicit; avoid large fixture "god objects".
- Use
yieldfixtures for teardown so cleanup is always paired with setup.
Parametrize and ID Naming¶
- Use
pytest.mark.parametrizefor 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 -quv run pytest -m unit -quv 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
unitdirectories without marker updates. - Sharing mutable module-level state across tests.