Pytest patterns¶
Status: Placeholder — to be developed. Last reviewed: — Reference structural sibling:
guidelines/i18n/translation-rules.md(process + rules style).
Scope (when this guideline lands)¶
Conventions for tests: directory layout, fixture scope, factory usage, integration vs. unit boundary, when to hit the real DB, naming, what make test / make test-k / make test-f / make test-x actually do.
Out of scope (cross-refs)¶
- Test data reset for development (
docker-reset-test-data.sh) →docs/RESET_TEST_DATA.md(reference). Test fixture data and dev test data are different concerns. - CI configuration (where tests run, environment setup) →
guidelines/development-workflow/overview.md(Draft, currently has the deploy info). - Migration testing (does
make migratework cleanly on a fresh DB?) →guidelines/backend/migrations.md(placeholder).
Sources to mine when writing this¶
conftest.py(root) and any per-appconftest.pyfiles. Document every fixture.pytest.iniconfiguration. Document every option.Makefiletest targets —test,test-seq,test-k,test-f,test-x,test-coverage. Document what each one is for.- Existing test factories (factory_boy or hand-rolled) — converge on one pattern.
- The recent
test_entity_holderfix (commit76c7d45— "pin entity name, not just code") — that's a worth-documenting lesson about test brittleness. - Per-app
tests/directories — survey for outliers.
Starter hard rules to investigate¶
- Pytest, not unittest: every test file uses pytest fixtures, not
TestCasesetUp/tearDown. - Factories over hand-rolled fixtures: every model has a factory in
apps/<app>/tests/factories.py. - Real DB integration over mocks for migration safety (per
MEMORY.mduser feedback: "Don't mock the database — we got burned when mocked tests passed but prod migration failed"). - Test names describe behaviour, not implementation:
test_user_with_no_practice_access_cannot_view_patient_list, nottest_user_view. pytest.mark.django_dbon every test that hits the DB, no exceptions — the speed cost is real but the implicit-DB confusion cost is higher.
Decision points to settle¶
- Fixture scope defaults: function vs module vs session. Codebase has all three; pick a default with clear "use X when Y" rules.
pytest.mark.slow: a marker for expensive tests (e.g. full Doctolib API replay). When to mark, how to skip in fast cycles (make test-fast?).- Mocking external services (Doctolib API, Pennylane, Sentry): when is mocking acceptable despite the "real-DB" rule? (Probably: external HTTP yes, internal DB no.)
- Test data realism: factory defaults that look like real data vs deliberately weird (Faker? curated minimal data? both?).
- Coverage targets:
make test-coverageexists — is there a minimum threshold? CI gate?
Known deviations to look for during writing¶
- Tests using
unittest.TestCaseinstead of pytest fixtures. - Tests that mock Django's ORM (the bug
MEMORY.mdwarns against). - Tests without
pytest.mark.django_dbthat silently fail-open. - Hand-rolled factories that should use factory_boy.
- Tests pinned to model field values that change with i18n source rewrites (a future i18n change risks breaking them — flag for refactor to use stable identifiers).
If found, file as roadmap/backlog/testing-pytest-drift-2026-MM.md.