Skip to content

Testing

Veloce ships an in-memory TestClient. It constructs ASGI scopes directly and runs your handlers through the real app.__call__ surface — middleware, response encoding, cookies, and the lifespan handshake all execute. No socket, no separate server.

Creating a client

app.test_client() returns a client bound to your application:

from veloce import Veloce

app = Veloce()


@app.get("/ping")
async def ping():
    return {"pong": True}


def test_ping():
    client = app.test_client()
    response = client.get("/ping")
    assert response.status_code == 200
    assert response.json() == {"pong": True}

You can also construct it explicitly:

from veloce.testclient import TestClient

client = TestClient(app)

Making requests

The client mirrors the HTTP verbs and returns a response object with status_code, json(), text, headers, and content_type:

client.get("/items", params={"page": 2})
client.post("/users", json={"name": "ada", "email": "ada@example.com"})
client.post("/upload", files={"f": ("data.csv", b"a,b,c")})
client.delete("/items/1")

Cookies set by the application persist across calls on the same client.

Lifespan startup and shutdown

Use the client as a context manager to run startup / shutdown events around the block:

def test_with_lifespan():
    with app.test_client() as client:
        # startup handlers have run here
        assert client.get("/ping").status_code == 200
    # shutdown handlers have run here

Testing WebSockets

websocket_connect opens a WebSocket against a @app.websocket route:

def test_ws_echo():
    client = app.test_client()
    with client.websocket_connect("/ws") as ws:
        ws.send_text("hello")
        assert ws.receive_text() == "echo: hello"

Overriding dependencies

Combine the client with app.dependency_overrides to swap real dependencies (databases, auth, external APIs) for fakes — see Dependency Injection.