Notice that the testing functions are normal def, not async def.
And the calls to the client are also normal calls, not using await.
This allows you to use pytest directly without complications.
Technical Details
You could also use from starlette.testclient import TestClient.
FastAPI provides the same starlette.testclient as fastapi.testclient just as a convenience for you, the developer. But it comes directly from Starlette.
Tip
If you want to call async functions in your tests apart from sending requests to your FastAPI application (e.g. asynchronous database functions), have a look at the Async Tests in the advanced tutorial.
You could then update test_main.py with the extended tests:
fromfastapi.testclientimportTestClientfrom.mainimportappclient=TestClient(app)deftest_read_item():response=client.get("/items/foo",headers={"X-Token":"coneofsilence"})assertresponse.status_code==200assertresponse.json()=={"id":"foo","title":"Foo","description":"There goes my hero",}deftest_read_item_bad_token():response=client.get("/items/foo",headers={"X-Token":"hailhydra"})assertresponse.status_code==400assertresponse.json()=={"detail":"Invalid X-Token header"}deftest_read_nonexistent_item():response=client.get("/items/baz",headers={"X-Token":"coneofsilence"})assertresponse.status_code==404assertresponse.json()=={"detail":"Item not found"}deftest_create_item():response=client.post("/items/",headers={"X-Token":"coneofsilence"},json={"id":"foobar","title":"Foo Bar","description":"The Foo Barters"},)assertresponse.status_code==200assertresponse.json()=={"id":"foobar","title":"Foo Bar","description":"The Foo Barters",}deftest_create_item_bad_token():response=client.post("/items/",headers={"X-Token":"hailhydra"},json={"id":"bazz","title":"Bazz","description":"Drop the bazz"},)assertresponse.status_code==400assertresponse.json()=={"detail":"Invalid X-Token header"}deftest_create_existing_item():response=client.post("/items/",headers={"X-Token":"coneofsilence"},json={"id":"foo","title":"The Foo ID Stealers","description":"There goes my stealer",},)assertresponse.status_code==409assertresponse.json()=={"detail":"Item already exists"}
Whenever you need the client to pass information in the request and you don't know how to, you can search (Google) how to do it in httpx, or even how to do it with requests, as HTTPX's design is based on Requests' design.
Then you just do the same in your tests.
E.g.:
To pass a path or query parameter, add it to the URL itself.
To pass a JSON body, pass a Python object (e.g. a dict) to the parameter json.
If you need to send Form Data instead of JSON, use the data parameter instead.
To pass headers, use a dict in the headers parameter.
For cookies, a dict in the cookies parameter.
For more information about how to pass data to the backend (using httpx or the TestClient) check the HTTPX documentation.
Info
Note that the TestClient receives data that can be converted to JSON, not Pydantic models.
If you have a Pydantic model in your test and you want to send its data to the application during testing, you can use the jsonable_encoder described in JSON Compatible Encoder.