Movatterモバイル変換


[0]ホーム

URL:


Skip to content
Join theFastAPI Cloud waiting list 🚀
Follow@fastapi onX (Twitter) to stay updated
FollowFastAPI onLinkedIn to stay updated
Subscribe to theFastAPI and friends newsletter 🎉
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor

Async Tests

You have already seen how to test yourFastAPI applications using the providedTestClient. Up to now, you have only seen how to write synchronous tests, without usingasync functions.

Being able to use asynchronous functions in your tests could be useful, for example, when you're querying your database asynchronously. Imagine you want to test sending requests to your FastAPI application and then verify that your backend successfully wrote the correct data in the database, while using an async database library.

Let's look at how we can make that work.

pytest.mark.anyio

If we want to call asynchronous functions in our tests, our test functions have to be asynchronous. AnyIO provides a neat plugin for this, that allows us to specify that some test functions are to be called asynchronously.

HTTPX

Even if yourFastAPI application uses normaldef functions instead ofasync def, it is still anasync application underneath.

TheTestClient does some magic inside to call the asynchronous FastAPI application in your normaldef test functions, using standard pytest. But that magic doesn't work anymore when we're using it inside asynchronous functions. By running our tests asynchronously, we can no longer use theTestClient inside our test functions.

TheTestClient is based onHTTPX, and luckily, we can use it directly to test the API.

Example

For a simple example, let's consider a file structure similar to the one described inBigger Applications andTesting:

.├── app│   ├── __init__.py│   ├── main.py│   └── test_main.py

The filemain.py would have:

fromfastapiimportFastAPIapp=FastAPI()@app.get("/")asyncdefroot():return{"message":"Tomato"}

The filetest_main.py would have the tests formain.py, it could look like this now:

importpytestfromhttpximportASGITransport,AsyncClientfrom.mainimportapp@pytest.mark.anyioasyncdeftest_root():asyncwithAsyncClient(transport=ASGITransport(app=app),base_url="http://test")asac:response=awaitac.get("/")assertresponse.status_code==200assertresponse.json()=={"message":"Tomato"}

Run it

You can run your tests as usual via:

$pytest---> 100%

In Detail

The marker@pytest.mark.anyio tells pytest that this test function should be called asynchronously:

importpytestfromhttpximportASGITransport,AsyncClientfrom.mainimportapp@pytest.mark.anyioasyncdeftest_root():asyncwithAsyncClient(transport=ASGITransport(app=app),base_url="http://test")asac:response=awaitac.get("/")assertresponse.status_code==200assertresponse.json()=={"message":"Tomato"}

Tip

Note that the test function is nowasync def instead of justdef as before when using theTestClient.

Then we can create anAsyncClient with the app, and send async requests to it, usingawait.

importpytestfromhttpximportASGITransport,AsyncClientfrom.mainimportapp@pytest.mark.anyioasyncdeftest_root():asyncwithAsyncClient(transport=ASGITransport(app=app),base_url="http://test")asac:response=awaitac.get("/")assertresponse.status_code==200assertresponse.json()=={"message":"Tomato"}

This is the equivalent to:

response=client.get('/')

...that we used to make our requests with theTestClient.

Tip

Note that we're using async/await with the newAsyncClient - the request is asynchronous.

Warning

If your application relies on lifespan events, theAsyncClient won't trigger these events. To ensure they are triggered, useLifespanManager fromflorimondmanca/asgi-lifespan.

Other Asynchronous Function Calls

As the testing function is now asynchronous, you can now also call (andawait) otherasync functions apart from sending requests to your FastAPI application in your tests, exactly as you would call them anywhere else in your code.

Tip

If you encounter aRuntimeError: Task attached to a different loop when integrating asynchronous function calls in your tests (e.g. when usingMongoDB's MotorClient), remember to instantiate objects that need an event loop only within async functions, e.g. an@app.on_event("startup") callback.


[8]ページ先頭

©2009-2026 Movatter.jp