Testing
The test client allows you to make requests against your ASGI application using the httpx library.
from starlette.responses import HTMLResponse
from starlette.testclient import TestClient
async def app(scope, receive, send):
assert scope['type'] == 'http'
response = HTMLResponse('<html><body>Hello, world!</body></html>')
await response(scope, receive, send)
def test_app():
client = TestClient(app)
response = client.get('/')
assert response.status_code == 200
The interface exposed by the test client is the same as any other httpx session. It is important to note that the call that makes the request is only a standard function call, not a waitable object.
You can use any of the httpx standard APIs, such as authentication, session Cookie handling, or file uploads.
For example, to set the header on TestClient, you can do this:
client = TestClient(app)
# Set headers on the client for future requests
client.headers = {"Authorization": "..."}
response = client.get("/")
# Set headers for each request separately
response = client.get("/", headers={"Authorization": "..."})
For example, using TestClient to send files:
client = TestClient(app)
# Send a single file
with open("example.txt", "rb") as f:
response = client.post("/form", files={"file": f})
# Send multiple files
with open("example.txt", "rb") as f1:
with open("example.png", "rb") as f2:
files = {"file1": f1, "file2": ("filename", f2, "image/png")}
response = client.post("/form", files=files)
For more information, you can check out the httpx documentation.
By default,TestClient throws any exception that occurs in your application. Occasionally, you may want to test the contents of a 500 error response rather than allow the client to throw a server exception. In this case, you should use client = TestClient(app, raise_server_exceptions=False).
Attention
If you want the TestClient to run the lifespan handler,
you will need to use the TestClient as a context manager. It will
not be triggered when the TestClient is instantiated. You can learn more about it
here.
Select asynchronous backend
TestClient accepts parameters backend(a string) and backend_options(a dictionary). These options are passed to anyio.start_blocking_portal(). For more information on acceptable backend options, see the anyio documentation. By default,asyncio and its default options are used.
Sorry, the meaning of "Trio" and "backend="trio"" in the original text is unclear and cannot be accurately translated. If you can provide more information about these two symbols, I will try my best to provide you with a more accurate translation.
def test_app()
with TestClient(app, backend="trio") as client:
...
To run asyncio using uvloop, pass backend_options={"use_uvloop": True}. For example:
def test_app()
with TestClient(app, backend_options={"use_uvloop": True}) as client:
...
Testing WebSocket Sessions
You can also test WebSocket sessions using a test client.
httpx The library will be used to build the initial handshake, which means you can use the same authentication options and other headers between http and websocket tests.
from starlette.testclient import TestClient
from starlette.websockets import WebSocket
async def app(scope, receive, send):
assert scope['type'] == 'websocket'
websocket = WebSocket(scope, receive=receive, send=send)
await websocket.accept()
await websocket.send_text('Hello, world!')
await websocket.close()
def test_app():
client = TestClient(app)
with client.websocket_connect('/') as websocket:
data = websocket.receive_text()
assert data == 'Hello, world!'
Operations on sessions are standard function calls, not waitable objects.
It is important to use sessions within the context-managed with block. This ensures that the background thread on which the ASGI application resides terminates correctly and that any exceptions that occur within the application are always raised by the test client.
Set up a test session
.websocket_connect(url, subprotocols=None, **options)-takes the same set of parameters ashttpx.get().
If the application does not accept WebSocket connections,starlette.websockets.WebSocketDisconnect may be thrown.
websocket_connect() must be used as context manager (in with block).
!!! Note that params parameters are not supported by websocket_connect. If you need to pass query parameters, hardcode them directly in the URL.
```python
with client.websocket_connect('/path?foo=bar') as websocket:
...
```
TXD
.send_text(data)- Sends the given text to the application..send_bytes(data)- Sends the given byte to the application..send_json(data, mode="text")-Send the given data to the application. Usemode="binary"to send JSON in binary data frames.
RXD
.receive_text()- Wait for incoming text sent by the application and return it..receive_bytes()- Waits for an incoming byte string sent by the application and returns it..receive_json(mode="text")-waits for incoming json data sent by the application and returns it. Usemode="binary"to receive JSON over binary data frames.
The text "May raise starlette.websockets.WebSocketDisconnect ."seems not to conform to the conventional semantic expression, it is difficult to accurately translate into Chinese. If you have more context information or confirm the accuracy of the text content, I will try my best to provide you with a more satisfactory translation starlette.websockets.WebSocketDisconnect But this translation may not be quite in line with the actual meaning.
close the connection
.close(code=1000)- Performs a client-side closure of WebSocket connections.
asynchronous test
Sometimes you want to perform asynchronous operations outside of your application. For example, you might want to check the status of your database after invoking your application using an existing asynchronous database client/infrastructure.
For these cases, using TestClient is difficult because it creates its own event loop, and asynchronous resources (such as database connections) often cannot be shared between event loops. The easiest way to fix this problem is to make your entire test asynchronous and use an asynchronous client, such as httpx.AsyncClient.
Here is an example of such a test:
from httpx import AsyncClient
from starlette.applications import Starlette
from starlette.routing import Route
from starlette.requests import Request
from starlette.responses import PlainTextResponse
def hello(request: Request) -> PlainTextResponse:
return PlainTextResponse("Hello World!")
app = Starlette(routes=[Route("/", hello)])
# if you're using pytest, you'll need to to add an async marker like:
# @pytest.mark.anyio # using https://github.com/agronholm/anyio
# or install and configure pytest-asyncio (https://github.com/pytest-dev/pytest-asyncio)
async def test_app() -> None:
# note: you _must_ set `base_url` for relative urls like "/" to work
async with AsyncClient(app=app, base_url="http://testserver") as client:
r = await client.get("/")
assert r.status_code == 200
assert r.text == "Hello World!"