Skip to content

Testing

The in-memory test clients.

TestClient

Sync test client - drives the app through its ASGI surface.

Usage::

client = TestClient(app)
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "ok"}

cookies property

cookies: _TestClientCookies

Live view of the client's cookie jar.

Supports dict-like access (client.cookies["session"]), assignment (client.cookies["k"] = "v"), deletion, iteration, and bulk clear(). Cookies the server sends on responses are automatically merged in via _update_cookies. The state persists across calls until the client is closed.

set_cookie(key: str, value: str) -> None

Add or update a cookie sent on every subsequent request.

delete_cookie(key: str) -> None

Remove a cookie from the jar. No-op if not present.

session_transaction

session_transaction() -> Any

Mutate the session outside a request.

Yields a Session dict pre-loaded from the current session cookie (if any). On block exit the session is re-signed with the app's SessionMiddleware secret and stored in the cookie jar, so the next request carries it::

with client.session_transaction() as sess:
    sess["user_id"] = 7

Raises RuntimeError if the app has no SessionMiddleware.

get

get(path: str, headers: dict[str, str] | None = None, params: dict[str, str] | Sequence[tuple[str, str]] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send a GET request and return the response.

post

post(path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Send a POST. stream feeds the body as multiple http.request chunks (a sync Iterable or AsyncIterable of bytes/str); when given it takes precedence over and excludes json/data/content/files.

put

put(path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Send a PUT. See post for the stream chunked-body parameter.

patch

patch(path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Send a PATCH. See post for the stream chunked-body parameter.

delete

delete(path: str, headers: dict[str, str] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send a DELETE request and return the response.

head

head(path: str, headers: dict[str, str] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send a HEAD request and return the response.

options

options(path: str, headers: dict[str, str] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send an OPTIONS request and return the response.

request

request(method: str, path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, params: dict[str, str] | Sequence[tuple[str, str]] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Generic request dispatcher - httpx/test-client shape.

client.request("PATCH", "/x", json=...) is the verb-agnostic form of client.get / client.post / .... Bodies (json / data / content / files / stream) and params are handled exactly as the per-verb methods do; stream (see post) excludes the buffered body forms.

websocket_connect

websocket_connect(path: str, subprotocols: list[str] | None = None, headers: dict[str, str] | None = None) -> _WebSocketSession

Open an in-memory WebSocket against the app - context manager.

Drives the ASGI websocket protocol: synthesise the scope, send websocket.connect, route to the handler, then forward send_text / receive_text / close calls to the running handler through a pair of asyncio queues.

close

close() -> None

Run shutdown lifecycle and close the loop if we own it.

AsyncTestClient

Async in-memory test client - drives the app through its ASGI surface.

The async counterpart of TestClient: used as an async context manager inside an async test, so each request is awaited on the test's own running event loop instead of through a private loop. The request methods (get / post / ...) are coroutines.

Usage::

async with AsyncTestClient(app) as client:
    resp = await client.get("/")

Cookie persistence, redirect following, and the JSON / form / files body shapes match TestClient exactly. WebSocket testing stays on the sync TestClient.websocket_connect.

cookies property

cookies: _TestClientCookies

Live view of the client's cookie jar (see TestClient.cookies).

set_cookie(key: str, value: str) -> None

Add or update a cookie sent on every subsequent request.

delete_cookie(key: str) -> None

Remove a cookie from the jar. No-op if not present.

get async

get(path: str, headers: dict[str, str] | None = None, params: dict[str, str] | Sequence[tuple[str, str]] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send a GET request and return the response.

post async

post(path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Send a POST. stream feeds the body as multiple http.request chunks (a sync Iterable or AsyncIterable of bytes/str); when given it takes precedence over and excludes json/data/content/files.

put async

put(path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Send a PUT. See post for the stream chunked-body parameter.

patch async

patch(path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Send a PATCH. See post for the stream chunked-body parameter.

delete async

delete(path: str, headers: dict[str, str] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send a DELETE request and return the response.

head async

head(path: str, headers: dict[str, str] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send a HEAD request and return the response.

options async

options(path: str, headers: dict[str, str] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send an OPTIONS request and return the response.

request async

request(method: str, path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, params: dict[str, str] | Sequence[tuple[str, str]] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Generic verb-agnostic request dispatcher (see TestClient.request).