Litestar

Status: First-class support

Litestar is the primary supported framework with full type extraction from route handlers.

Installation

pip install "pytest-routes[litestar]"
uv add "pytest-routes[litestar]"

Features

  • Full path parameter type extraction from route definitions

  • Query parameter extraction from handler signatures with types

  • Request body type extraction (Pydantic models, dataclasses, TypedDicts)

  • Handler metadata (tags, deprecated, description)

  • OpenAPI schema fallback for edge cases

Complete Example

# myapp/main.py
from dataclasses import dataclass
from uuid import UUID

from litestar import Litestar, Controller, get, post, put, delete

@dataclass
class User:
    """User model with validation."""
    id: UUID
    name: str
    email: str
    is_active: bool = True

@dataclass
class CreateUser:
    """Input model for creating users."""
    name: str
    email: str

@dataclass
class UpdateUser:
    """Input model for updating users."""
    name: str | None = None
    email: str | None = None
    is_active: bool | None = None

class UserController(Controller):
    """User management endpoints."""

    path = "/users"
    tags = ["users"]

    @get("/")
    async def list_users(
        self,
        limit: int = 10,
        offset: int = 0,
        active_only: bool = False
    ) -> list[User]:
        """List all users with pagination."""
        return []

    @get("/{user_id:uuid}")
    async def get_user(self, user_id: UUID) -> User:
        """Get a specific user by ID."""
        return User(id=user_id, name="Test", email="test@example.com")

    @post("/")
    async def create_user(self, data: CreateUser) -> User:
        """Create a new user."""
        return User(id=UUID("..."), name=data.name, email=data.email)

    @put("/{user_id:uuid}")
    async def update_user(self, user_id: UUID, data: UpdateUser) -> User:
        """Update an existing user."""
        return User(id=user_id, name="Updated", email="updated@example.com")

    @delete("/{user_id:uuid}")
    async def delete_user(self, user_id: UUID) -> None:
        """Delete a user."""
        pass

app = Litestar(route_handlers=[UserController], debug=True)

Running Tests

# Basic smoke test
pytest --routes --routes-app myapp.main:app

# With verbose output
pytest --routes --routes-app myapp.main:app -v

# Test only user endpoints
pytest --routes --routes-app myapp.main:app --routes-include "/users/*"

What pytest-routes Extracts

For the example above, pytest-routes discovers:

GET  /users/              params: limit(int), offset(int), active_only(bool)
GET  /users/{user_id}     params: user_id(UUID)
POST /users/              body: CreateUser
PUT  /users/{user_id}     params: user_id(UUID), body: UpdateUser
DELETE /users/{user_id}   params: user_id(UUID)

Tips

Tip

Use dataclasses or Pydantic models for best results. pytest-routes can generate valid test data for these automatically.

Tip

Litestar’s path parameter syntax ({user_id:uuid}) is fully supported. The type hint after : is used for strategy selection.

Warning

Guards and middleware are executed during smoke tests. If you have authentication guards, either exclude those routes or configure test auth.

Handling Authentication

# conftest.py
import pytest

@pytest.fixture
def app():
    """Create app with test authentication."""
    from myapp.main import app
    from myapp.auth import TestAuthMiddleware

    app.middleware = [TestAuthMiddleware()]
    return app