Spartan-ai-toolkit python-testing-strategies
Testing patterns for FastAPI with pytest-asyncio, httpx AsyncClient, fixtures, and test data factories. Use when writing tests, setting up test infrastructure, or improving coverage in a FastAPI project.
git clone https://github.com/c0x12c/ai-toolkit
T=$(mktemp -d) && git clone --depth=1 https://github.com/c0x12c/ai-toolkit "$T" && mkdir -p ~/.claude/skills && cp -r "$T/toolkit/skills/python-testing-strategies" ~/.claude/skills/spartan-stratos-spartan-ai-toolkit-python-testing-strategies && rm -rf "$T"
toolkit/skills/python-testing-strategies/SKILL.mdPython Testing Strategies — Quick Reference
Setup (CRITICAL)
Set
asyncio_mode = "auto" in pyproject.toml to auto-detect async tests without markers. (The default "strict" mode also works if you mark tests with @pytest.mark.asyncio.)
See examples.md for the complete conftest.py and pyproject.toml setup.
Test Client
Use
httpx.AsyncClient + ASGITransport — never TestClient for async tests.
See examples.md for client fixture setup.
Required Coverage Per Endpoint
- Happy path (create, read, update, delete)
- Not found (404)
- Validation error (422)
- Auth failure (401) — if protected
- Soft delete — deleted records not returned
Test Naming
async def test_create_item(): # Happy path async def test_create_item_missing_name(): # Validation async def test_get_item_not_found(): # 404 async def test_get_item_soft_deleted(): # Soft delete
Test Data Factories
Use simple factory functions with sensible defaults. Override only what the test cares about.
See examples.md for factory patterns.
Running Tests
pytest # All tests pytest tests/test_items.py # One file pytest -k "test_create" # By name pattern pytest --tb=short -q # Quiet output
Gotchas
-
Missing
silently breaks everything. Tests appear to pass (0 collected) or hang indefinitely. Addasyncio_mode = "auto"
to[tool.pytest.ini_options] asyncio_mode = "auto"
before writing any async test. This is the #1 cause of "my tests don't run."pyproject.toml -
andTestClient
are not interchangeable.AsyncClient
(from Starlette) is synchronous — it blocks.TestClient
(from httpx) is async — it uses the event loop. If your app usesAsyncClient
routes withasync def
, you must useawait
withAsyncClient
. UsingASGITransport
may hide async bugs because it runs synchronously.TestClient -
Database state leaks between tests without proper cleanup. Each test needs a clean database. Use an
fixture that creates/drops tables, or truncate between tests. Without this, test order matters and random failures appear in CI.autouse=True -
returns snake_case from FastAPI by default. If your Pydantic models useresponse.json()
, the JSON keys may differ from your Python field names. Always assert against the actual JSON keys, not the Python attribute names.alias_generator -
Forgetting to
in async tests gives confusing errors. If you seeawait
in test output instead of actual data, you forgot an<coroutine object ...>
. Everyawait
,client.get()
, etc. must be awaited.client.post()