Part of The Coercion Saga — making AI write quality code.
The Behavior Gate
Quality gates catch syntax. Tests catch behavior. A function that type-checks but returns garbage is worse than one that fails to compile.
The Async Trap
FastAPI is async. Get testing wrong and tests hang or miss real bugs.
# conftest.py
import asyncio
import pytest
@pytest.fixture(scope="session")
def event_loop():
loop = asyncio.new_event_loop()
loop.set_debug(True) # Catches blocking calls
yield loop
loop.close()
set_debug(True) catches time.sleep() instead of asyncio.sleep(), synchronous I/O, libraries that claim async but aren't. One line, whole category of bugs.
Test Client
@pytest.fixture
async def client(session: AsyncSession) -> AsyncGenerator[AsyncClient, None]:
app.dependency_overrides[get_session] = lambda: session
async with AsyncClient(app=app, base_url="http://test") as client:
yield client
app.dependency_overrides.clear()
Endpoint code doesn't know it's being tested. Runs exactly like production.
What to Test
Service layer:
async def test_create_user(session: AsyncSession):
user = await create_user(session, email="test@example.com", password="secret")
assert user.password != "secret" # Should be hashed
Error cases:
async def test_register_duplicate(client: AsyncClient, session: AsyncSession):
await create_user(session, email="taken@example.com", password="secret")
await session.commit()
response = await client.post("/auth/register", json={
"email": "taken@example.com", "password": "different"
})
assert response.status_code == 409 # Not 500
409 Conflict means you caught it intentionally. 500 means you caught it in a crash.
The Gate
test:backend:
stage: test
image: python:3.12-slim
services:
- postgres:16
variables:
DATABASE_URL: postgresql+asyncpg://postgres:postgres@db:5432/test
before_script:
- pip install uv && cd backend && uv sync --frozen
- uv run alembic upgrade head
script:
- uv run pytest -v --cov=app --cov-report=term-missing --cov-report=html
allow_failure: false
Copy, paste, adapt. It works.
The Point
Tests are the safety net. Refactor with confidence. Ship knowing the code works.
That's the deal.
Next up: [Frontend Tests] -coming soon- — Now prove the UI works too.
Top comments (0)