Test Writer — Python
Write clear, maintainable tests for a Python/FastAPI hexagonal backend with pytest and pytest-asyncio.
Use this skill when
- Testing a use case, adapter, route, or async flow in a Python backend
- You need fixtures for external adapters (email, Stripe, S3)
- You're unsure whether to mock or use the real implementation
Do not use this skill when
- The task is project structure → use
hexagonal-python-patterns
- The task is async patterns design → use
async-python-patterns
- The task is React or NestJS testing → use
test-writer-react / test-writer-nestjs
🎯 Golden Rule (non-negotiable)
- Real implementations for ALL internal components (repositories, services, use cases, domain objects)
- Mocks ONLY for outbound adapters toward external systems (third-party APIs, email, S3, Stripe, payment gateways)
- Real infrastructure via testcontainers for integration tests against real Postgres / Redis / Kafka / RabbitMQ / LocalStack — see
references/testcontainers.md
A fake/stub that diverges silently from the real implementation produces tests that pass but don't detect real regressions. Mocking only the external boundary keeps cost low and confidence high.
🎯 Workflow
- Ask for context — which use case or component needs testing?
- Read the source code — understand the interface and expected behavior.
- Classify dependencies — internal → real impl; external → mock via fixtures.
- Load templates —
references/conftest.md for db session fixtures; references/use-case-test.md for the AAA pattern; references/external-fixtures.md for mock factories; references/testcontainers.md for integration tests against real infrastructure.
- Write tests — AAA pattern (Arrange, Act, Assert), explicit names, one logical assert per test.
- Run —
uv run pytest (see references/commands.md).
🛡️ Edge cases (mandatory coverage)
Every test suite MUST cover edge cases, not just the happy path. For each use case / adapter / route under test, include tests for:
- Null / None / undefined inputs — pass
None where a value is expected, assert the correct error is raised
- Empty / boundary values — empty list
[], empty string "", 0, negative numbers, datetime.min / datetime.max, single-element collections
- Off-by-one boundaries — first/last index, page 1, page size boundary, offset equals total count
- Invalid / malformed input — wrong type, schema validation failure, out-of-range enum value, oversized payload
- Concurrency / race conditions — duplicate concurrent requests, idempotency key replay (when applicable)
- External adapter failure — outbound adapter raises, returns error, times out, returns empty — assert the use case handles it gracefully (no silent swallow)
- State transitions — already-exists, not-found, already-deleted, duplicate creation
If the feature under test has domain invariants or business rules, add at least one test per invariant that violates it and asserts the correct domain error.
What you never do
- Write an
InMemoryXxxRepository or any other fake for an internal implementation
- Mock a domain class, use case, or domain object
- Mock an internal repository
- Write a test that verifies an internal interaction (spy on an internal method) rather than an observable behavior
- Mock something just to make a test pass
Related skills
hexagonal-python-patterns — project structure and layer rules
async-python-patterns — async design patterns (test the patterns you implement)
References
references/conftest.md — pytest fixtures (in-memory SQLite session) and test structure
references/use-case-test.md — testing a use case with real repo + mocked external, AAA pattern
references/external-fixtures.md — mock factories for external adapters (email, Stripe, S3)
references/testcontainers.md — real infrastructure for integration tests (Postgres, Redis, Kafka, RabbitMQ, LocalStack)
references/commands.md — pytest commands (run, watch, coverage, single test)
1---2name: test-writer-python3description: Write unit and integration tests for Python/FastAPI apps with pytest + pytest-asyncio. Golden rule: real implementations for internal components, mocks only for outbound external adapters (email, Stripe, S3), testcontainers for real Postgres/Redis/Kafka. Trigger on: pytest, "write tests", "add tests", "test the use case/adapter/route", or any testing task in a Python backend. Use when testing any Python or FastAPI code.4---56# Test Writer — Python78Write clear, maintainable tests for a Python/FastAPI hexagonal backend with pytest and pytest-asyncio.910## Use this skill when11- Testing a use case, adapter, route, or async flow in a Python backend12- You need fixtures for external adapters (email, Stripe, S3)13- You're unsure whether to mock or use the real implementation1415## Do not use this skill when16- The task is project structure → use `hexagonal-python-patterns`17- The task is async patterns design → use `async-python-patterns`18- The task is React or NestJS testing → use `test-writer-react` / `test-writer-nestjs`1920## 🎯 Golden Rule (non-negotiable)21- **Real implementations** for ALL internal components (repositories, services, use cases, domain objects)22- **Mocks** ONLY for outbound adapters toward external systems (third-party APIs, email, S3, Stripe, payment gateways)23- **Real infrastructure** via testcontainers for integration tests against real Postgres / Redis / Kafka / RabbitMQ / LocalStack — see `references/testcontainers.md`2425A fake/stub that diverges silently from the real implementation produces tests that pass but don't detect real regressions. Mocking only the external boundary keeps cost low and confidence high.2627## 🎯 Workflow281. **Ask for context** — which use case or component needs testing?292. **Read the source code** — understand the interface and expected behavior.303. **Classify dependencies** — internal → real impl; external → mock via fixtures.314. **Load templates** — `references/conftest.md` for db session fixtures; `references/use-case-test.md` for the AAA pattern; `references/external-fixtures.md` for mock factories; `references/testcontainers.md` for integration tests against real infrastructure.325. **Write tests** — AAA pattern (Arrange, Act, Assert), explicit names, one logical assert per test.336. **Run** — `uv run pytest` (see `references/commands.md`).3435## 🛡️ Edge cases (mandatory coverage)36Every test suite MUST cover edge cases, not just the happy path. For each use case / adapter / route under test, include tests for:37- **Null / None / undefined inputs** — pass `None` where a value is expected, assert the correct error is raised38- **Empty / boundary values** — empty list `[]`, empty string `""`, `0`, negative numbers, `datetime.min` / `datetime.max`, single-element collections39- **Off-by-one boundaries** — first/last index, page 1, page size boundary, offset equals total count40- **Invalid / malformed input** — wrong type, schema validation failure, out-of-range enum value, oversized payload41- **Concurrency / race conditions** — duplicate concurrent requests, idempotency key replay (when applicable)42- **External adapter failure** — outbound adapter raises, returns error, times out, returns empty — assert the use case handles it gracefully (no silent swallow)43- **State transitions** — already-exists, not-found, already-deleted, duplicate creation4445If the feature under test has domain invariants or business rules, add at least one test per invariant that violates it and asserts the correct domain error.4647## What you never do48- Write an `InMemoryXxxRepository` or any other fake for an internal implementation49- Mock a domain class, use case, or domain object50- Mock an internal repository51- Write a test that verifies an internal interaction (spy on an internal method) rather than an observable behavior52- Mock something just to make a test pass5354## Related skills55- `hexagonal-python-patterns` — project structure and layer rules56- `async-python-patterns` — async design patterns (test the patterns you implement)5758## References59- `references/conftest.md` — pytest fixtures (in-memory SQLite session) and test structure60- `references/use-case-test.md` — testing a use case with real repo + mocked external, AAA pattern61- `references/external-fixtures.md` — mock factories for external adapters (email, Stripe, S3)62- `references/testcontainers.md` — real infrastructure for integration tests (Postgres, Redis, Kafka, RabbitMQ, LocalStack)63- `references/commands.md` — pytest commands (run, watch, coverage, single test)