diff options
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 |
