diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-22 15:07:32 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-22 15:07:32 -0500 |
| commit | 7f17f6116f846ce0817da2caca4855cc7e72d576 (patch) | |
| tree | 8fc1bb3602b12a9e7117866bb9e22c2ce4044401 /languages/python/claude | |
| parent | 6a0f6d6517e2434f9249fc5e98ae3d0b5e23e45f (diff) | |
| download | rulesets-7f17f6116f846ce0817da2caca4855cc7e72d576.tar.gz rulesets-7f17f6116f846ce0817da2caca4855cc7e72d576.zip | |
docs(languages): revise python-testing SQLite and ORM-mocking guidance
Two audit fixes. The "prefer in-memory SQLite" advice is risky when prod is Postgres or MySQL — SQLite diverges on query semantics, constraints, transactions, JSON, time zones, and indexes, so a test can pass on SQLite and fail in prod. ORM/query tests now use a production-like DB, with SQLite reserved for pure unit tests. The "never mock the ORM" rule is also clarified: don't mock ORM internals, but a thin orchestration unit can inject a fake at a deliberate data-access port it owns.
Diffstat (limited to 'languages/python/claude')
| -rw-r--r-- | languages/python/claude/rules/python-testing.md | 15 |
1 files changed, 13 insertions, 2 deletions
diff --git a/languages/python/claude/rules/python-testing.md b/languages/python/claude/rules/python-testing.md index d2342c1..4edde35 100644 --- a/languages/python/claude/rules/python-testing.md +++ b/languages/python/claude/rules/python-testing.md @@ -92,12 +92,18 @@ to skip. - Email sending (Django: `django.core.mail.outbox`) ### Never mock these (internal domain): -- ORM queries (SQLAlchemy, Django ORM) +- ORM internals (querysets, sessions, model internals) - Model methods and properties - Form and serializer validation - Middleware - Your own service functions +For domain services, use real model methods and validation — don't mock the +ORM. But a thin orchestration unit can be cleaner to test with a fake injected +at a deliberate data-access port (a repository or interface the code owns). +That's still mocking at a boundary, not at internals: replace the port you +defined, not the ORM's querysets, sessions, or model internals underneath it. + ## Async Testing Use `anyio` for async tests (not raw `asyncio`): @@ -113,5 +119,10 @@ async def test_process_order_async(): - Mark database tests with `@pytest.mark.django_db` - Use transactions for isolation (pytest-django default) -- Prefer in-memory SQLite for speed in unit tests +- Run ORM/query tests against a production-like database — the same engine + as prod (a test Postgres or MySQL, often containerized). SQLite differs + from Postgres/MySQL on query semantics, constraints, transactions, JSON + columns, time zones, and indexes, so a test can pass on SQLite and fail + in production. Reserve in-memory SQLite for pure unit tests that don't + depend on database semantics. - Use `select_related` / `prefetch_related` assertions to catch N+1 regressions |
