Framework Integration Testing¶
This page shows how to test dioxide-based applications with popular Python frameworks.
FastAPI Integration¶
dioxide integrates seamlessly with FastAPI for testing.
Basic Test Setup¶
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from httpx import AsyncClient, ASGITransport
from dioxide import Container, Profile
from dioxide.testing import fresh_container
@pytest.fixture
async def app():
"""Create FastAPI app with test profile."""
from myapp.main import create_app
async with fresh_container(profile=Profile.TEST) as container:
app = create_app(container)
yield app
@pytest.fixture
async def client(app):
"""Async test client for FastAPI."""
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://test"
) as client:
yield client
Testing Endpoints¶
async def test_create_user(client, app):
"""Test user creation endpoint."""
response = await client.post(
"/users",
json={"name": "Alice", "email": "alice@example.com"}
)
assert response.status_code == 201
data = response.json()
assert data["name"] == "Alice"
async def test_welcome_email_sent(client, app):
"""Test that welcome email is sent on registration."""
# Get the fake email adapter from the container
container = app.state.container
email = container.resolve(EmailPort)
# Make request
await client.post(
"/users",
json={"name": "Alice", "email": "alice@example.com"}
)
# Verify email was sent
assert len(email.sent_emails) == 1
assert email.sent_emails[0]["to"] == "alice@example.com"
Using DioxideMiddleware¶
If you’re using DioxideMiddleware, testing is straightforward:
from fastapi import FastAPI
from dioxide.fastapi import DioxideMiddleware, Inject
import pytest
from httpx import AsyncClient, ASGITransport
@pytest.fixture
def app():
"""Create app with test profile middleware."""
app = FastAPI()
app.add_middleware(
DioxideMiddleware,
profile=Profile.TEST,
packages=["myapp"]
)
@app.get("/users/{user_id}")
async def get_user(
user_id: int,
service: UserService = Inject(UserService)
):
return await service.get_user(user_id)
return app
@pytest.fixture
async def client(app):
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://test"
) as client:
yield client
async def test_get_user(client):
response = await client.get("/users/1")
assert response.status_code == 200
Flask Integration¶
Testing Flask applications with dioxide.
Basic Test Setup¶
import pytest
from flask import Flask
from dioxide import Container, Profile
@pytest.fixture
def app():
"""Create Flask app with test profile."""
app = Flask(__name__)
container = Container()
container.scan(profile=Profile.TEST)
app.container = container
@app.route("/users", methods=["POST"])
def create_user():
service = app.container.resolve(UserService)
# ...
return app
@pytest.fixture
def client(app):
"""Flask test client."""
return app.test_client()
Testing Endpoints¶
def test_create_user(client, app):
"""Test user creation."""
response = client.post(
"/users",
json={"name": "Alice", "email": "alice@example.com"}
)
assert response.status_code == 201
# Verify email was sent
email = app.container.resolve(EmailPort)
assert len(email.sent_emails) == 1
Django Integration¶
Testing Django applications with dioxide.
Basic Test Setup¶
import pytest
from django.test import Client, override_settings
from dioxide import Profile
from dioxide.django import configure_dioxide
@pytest.fixture(autouse=True)
def setup_dioxide():
"""Configure dioxide with test profile for each test."""
configure_dioxide(profile=Profile.TEST, packages=["myapp"])
yield
# Cleanup is automatic
@pytest.fixture
def client():
return Client()
Testing Views¶
def test_user_list(client):
"""Test user list view."""
response = client.get("/users/")
assert response.status_code == 200
def test_create_user(client):
"""Test user creation."""
from dioxide.django import inject
response = client.post(
"/users/",
data={"name": "Alice", "email": "alice@example.com"},
content_type="application/json"
)
assert response.status_code == 201
# Verify side effects
email = inject(EmailPort)
assert len(email.sent_emails) == 1
Celery Integration¶
Testing Celery tasks with dioxide.
Basic Test Setup¶
import pytest
from celery import Celery
from dioxide import Container, Profile
@pytest.fixture
def celery_app():
"""Create Celery app with test config."""
app = Celery("test")
app.config_from_object({
"task_always_eager": True, # Execute tasks synchronously
"task_eager_propagates": True, # Propagate exceptions
})
return app
@pytest.fixture
def container():
"""Container with test fakes."""
c = Container()
c.scan(profile=Profile.TEST)
return c
Testing Tasks¶
from myapp.tasks import process_order
def test_process_order(container, celery_app):
"""Test order processing task."""
# Seed test data
orders = container.resolve(OrderRepository)
orders.seed({"id": 1, "status": "pending", "total": 100.00})
# Run task (synchronously due to task_always_eager)
result = process_order.delay(order_id=1)
# Verify order was processed
order = orders.find_by_id(1)
assert order["status"] == "completed"
# Verify email was sent
email = container.resolve(EmailPort)
assert len(email.sent_emails) == 1
Click CLI Integration¶
Testing Click CLI commands with dioxide.
Basic Test Setup¶
import pytest
from click.testing import CliRunner
from dioxide import Container, Profile
@pytest.fixture
def runner():
"""Click test runner."""
return CliRunner()
@pytest.fixture
def container():
"""Container with test fakes."""
c = Container()
c.scan(profile=Profile.TEST)
return c
Testing Commands¶
from myapp.cli import cli
def test_send_newsletter(runner, container):
"""Test newsletter command."""
# Seed test data
users = container.resolve(UserRepository)
users.seed(
{"id": 1, "email": "alice@example.com", "subscribed": True},
{"id": 2, "email": "bob@example.com", "subscribed": True},
)
# Run command
result = runner.invoke(cli, ["send-newsletter", "--subject", "Hello!"])
assert result.exit_code == 0
# Verify emails were sent
email = container.resolve(EmailPort)
assert len(email.sent_emails) == 2
General Testing Tips¶
Test Isolation¶
Always use fresh containers for test isolation:
@pytest.fixture
async def container():
"""Fresh container per test."""
async with fresh_container(profile=Profile.TEST) as c:
yield c
Seeding Test Data¶
Use fake adapter methods to seed test data:
@pytest.fixture
def seeded_users(container):
"""Seed users for tests that need them."""
users = container.resolve(UserRepository)
users.seed(
{"id": 1, "email": "alice@example.com"},
{"id": 2, "email": "bob@example.com"},
)
return users
Verifying Side Effects¶
Access fakes directly to verify side effects:
async def test_sends_email(container):
service = container.resolve(UserService)
await service.register("alice@example.com", "Alice")
# Get fake adapter and verify
email = container.resolve(EmailPort)
assert len(email.sent_emails) == 1
assert email.sent_emails[0]["to"] == "alice@example.com"
See also
Test Fixtures - Test fixtures and container patterns
Testing Patterns - Common fake implementation patterns
dioxide.fastapi - FastAPI integration API
dioxide.flask - Flask integration API
dioxide.celery - Celery integration API
dioxide.click - Click integration API