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
¶
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
¶
Add or update a cookie sent on every subsequent request.
wait_for_background_tasks
¶
Wait for the app's background tasks to finish, on this client's loop.
A response's background task runs after the response is returned, so a
test asserting its effect otherwise has to reach for client._loop and
drive it with a hand-picked number of turns. Returns True when
everything finished, False on timeout.
Usage::
client.post("/subscribe")
client.wait_for_background_tasks()
assert sent == ["welcome@example.com"]
session_transaction
¶
session_transaction() -> Iterator[Session]
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: StreamBody | 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: StreamBody | 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: StreamBody | 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: StreamBody | None = None) -> TestResponse
Dispatch a request of any verb - 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.
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, and seeding a session outside
a request stays on the sync TestClient.session_transaction.
cookies
property
¶
Live view of the client's cookie jar (see TestClient.cookies).
wait_for_background_tasks
async
¶
Wait for the app's background tasks to finish. See TestClient.
set_cookie
¶
Add or update a cookie sent on every subsequent request.
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: StreamBody | 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: StreamBody | 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: StreamBody | 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: StreamBody | None = None) -> TestResponse
Dispatch a request of any verb (see TestClient.request).
TestResponse
¶
What every TestClient / AsyncTestClient call returns.
A read-only view of one ASGI response cycle: status_code, the decoded
text and parsed json(), a case-insensitive headers mapping, the parsed
cookies, and raw_headers for the cases the mapping flattens (several
Set-Cookie lines arrive as one joined value in headers, and separately in
raw_headers).
Not constructed directly - the clients build it.
Usage::
with TestClient(app) as client:
response = client.get("/items/1")
assert response.status_code == 200
assert response.json() == {"id": 1}
assert response.headers["content-type"].startswith("application/json")