| name | python-testing |
| description | Write or modify Python tests in Oscilla. Use when: adding new tests, understanding testing conventions, working with fixtures, writing FastAPI route tests, database tests, or game engine tests. Covers pytest patterns, fixture structure, and the game engine testing constraints. |
Python Testing
context7: If the mcp_context7 tool is available, resolve and load the full pytest documentation before creating new tests or running pytests commands not in the makefile:
mcp_context7_resolve-library-id: "pytest"
mcp_context7_get-library-docs: <resolved-id>
Guidelines and patterns for writing tests in the Oscilla codebase.
General Rules
- No test classes unless there is a specific technical reason. Prefer standalone functions.
- All fixtures must be defined or imported in
conftest.py so they are automatically available to all tests in that directory.
- No mocks for simple dataclasses or Pydantic models ā construct an instance directly with the desired parameters instead.
- Test file structure mirrors the main code ā a test for
oscilla/engine/foo.py lives at tests/engine/test_foo.py.
Running Tests
make pytest
make pytest_loud
uv run pytest
uv run pytest tests/engine/test_foo.py -k test_my_function -s
FastAPI Tests
Use the FastAPI TestClient via a fixture rather than calling router classes directly.
import pytest
from fastapi.testclient import TestClient
from oscilla.www import create_app
@pytest.fixture
def client() -> TestClient:
app = create_app()
return TestClient(app)
def test_get_post(client: TestClient) -> None:
response = client.get("/api/posts/some-uuid")
assert response.status_code == 200
Database Tests
Use a memory-backed SQLite fixture. Wire it into the FastAPI app via a dependency override so routes use the test database automatically.
import pytest
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
from oscilla.models.base import Base
from oscilla.dependencies.database import get_session
@pytest.fixture
async def db_session() -> AsyncSession:
engine = create_async_engine("sqlite+aiosqlite:///:memory:")
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async with async_session() as session:
yield session
await engine.dispose()
@pytest.fixture
def client(db_session: AsyncSession) -> TestClient:
app = create_app()
app.dependency_overrides[get_session] = lambda: db_session
return TestClient(app)
Game Engine Testing
The game engine has strict isolation requirements to keep tests independent of live content.
Critical Rules
- Engine tests must never reference
content/. Replacing or modifying the content package must never break any test.
- Unit tests (conditions, player state, step handlers) ā construct Pydantic models and dataclasses directly in Python. No YAML loading required.
- Integration tests that exercise the loader or full pipeline ā use minimal fixtures from
tests/fixtures/content/<scenario>/.
Fixture Structure
Each test scenario gets its own subdirectory:
tests/fixtures/content/
āāā basic-region/
ā āāā game.yaml
ā āāā regions/
ā āāā test-region-root.yaml
āāā item-pickup/
ā āāā ...
Naming rules for fixture manifests:
- Use
test- prefix followed by the kind or role (e.g., test-enemy, test-item, test-region-root)
- Never use content-flavored names (e.g., never
test-goblin, test-sword, test-dark-forest)
- This keeps fixtures structurally descriptive and prevents building narrative coherence inside the test suite
apiVersion: oscilla/v1
kind: Enemy
metadata:
name: test-enemy
spec:
health: 10
damage: 2
mock_tui Fixture
The mock_tui fixture in conftest.py must be used to drive all pipeline tests. No test should produce real terminal output.
def test_adventure_runs(mock_tui: MockTUI) -> None:
...
Fixture Conventions
- Fixtures shared across multiple test files ā
tests/conftest.py
- Fixtures specific to a subdirectory ā
tests/<subdirectory>/conftest.py
- Complex fixture content (YAML files, etc.) ā
tests/fixtures/
Style Checklist
Further Reading