Kludex / fastapi-tips

FastAPI Tips by The FastAPI Expert!
1.96k stars 74 forks source link

AsyncClient app parameter deprecation #16

Closed TheoBabilon closed 5 months ago

TheoBabilon commented 5 months ago

Hi @Kludex ,

Not sure if relevant, but whilst following tip 5 Use HTTPX's AsyncClient instead of TestClient in one of my current FastAPIs, I was only receiving 302s status codes:

async def test_client():
    async with AsyncClient(app=dividend_app, base_url="http://testserver") as client:
        resp = await client.get("/user/me")

getting

(Pdb++) resp
<Response [302 Found]>
(Pdb++) resp.text
'<HTML>\r\n<HEAD><TITLE>Redirection</TITLE></HEAD>\r\n<BODY><H1>Redirect</H1></BODY>\r\n</HTML>\r\n'

I can reproduce from current README's MCVE. Steps to reproduce:

uv venv
source .venv/bin/activate
uv pip install fastapi[all]

Then running mcve.py:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def read_root():
    return {"Hello": "World"}

# Using TestClient
from starlette.testclient import TestClient

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

# Using AsyncClient
import anyio
from httpx import AsyncClient

async def main():
    async with AsyncClient(app=app, base_url="http://test") as client:
        response = await client.get("/")
        assert response.status_code == 200
        assert response.json() == {"Hello": "World"}

anyio.run(main)

gives

  File "/path/to/mcve.py", line 29, in main
    assert response.status_code == 200
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError

status_code is indeed 302.

I believe it comes from httpx's AsyncClient app parameter deprecation in 0.27.0.

Changing to

async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client

fixes the issue, making the above MCVE pass.

FWIW, uv pip list gives

$> uv pip list
Package              Version
-------------------- --------
annotated-types      0.6.0
anyio                4.3.0
certifi              2024.2.2
click                8.1.7
dnspython            2.6.1
email-validator      2.1.1
fastapi              0.110.3
h11                  0.14.0
httpcore             1.0.5
httptools            0.6.1
httpx                0.27.0
idna                 3.7
itsdangerous         2.2.0
jinja2               3.1.3
markupsafe           2.1.5
orjson               3.10.2
pydantic             2.7.1
pydantic-core        2.18.2
pydantic-extra-types 2.7.0
pydantic-settings    2.2.1
python-dotenv        1.0.1
python-multipart     0.0.9
pyyaml               6.0.1
sniffio              1.3.1
starlette            0.37.2
typing-extensions    4.11.0
ujson                5.9.0
uvicorn              0.29.0
uvloop               0.19.0
watchfiles           0.21.0
websockets           12.0
Kludex commented 5 months ago

It's relevant, and the example needs to be updated to use the ASGITransport. But... If you add a slash at the end of the /user/me/, does still redirects?

In any case, PR welcome to add the ASGITransport. :)

TheoBabilon commented 5 months ago

Can confirm that status_code is still 302 when targeting /user/me/, so the trailing slash makes no difference here