Cookies not setting on starlette TestClient, with requests sent via Python Requests

Viewed 2334

The login/logout feature on FastAPI works in the browser, but I am trying to write unit tests for it. When my app sets the cookie, I can see the response DOES have the cookie sent. When I receive it with Python Request, the cookie has been stripped from the response hence the login doesn't work.

@pytest.fixture(scope='module')
def test_client():
    app = create_app(testing=True)
    client = TestClient(app)
    client.base_url = 'https://localhost'
    from app.models import Base, User
    Base.metadata.create_all(bind=engine)
    yield client
    db.flush()
    Base.metadata.drop_all(bind=engine)

# Simple login functions.
def test_login(test_client):
    response = test_client.post(url='/login', data=dict(
        username=username,
        password=password
    ), allow_redirects=True, proxies=proxies)
    breakpointB()
    assert response.headers

Then the server side which works in the browser:

    @core.post("/login", response_model=schemas.Token)
    async def login_for_access_token(*, request: Request, form_data: OAuth2PasswordRequestForm = Depends(),
                                     db: Session = Depends(get_db)):
        token = jsonable_encoder(access_token)
        response = RedirectResponse(url=request.url_for('index'), status_code=303)
        response.set_cookie(
            "Authorization",
            value=f"Bearer {token}",
            domain=os.environ.get('DOMAIN'),
            httponly=False,
            max_age=1800,
            expires=1800,
        )
        breakpointA()
        return response

So at BreakpointA(), just before the response is sent, this is what response.headers looks like:

MutableHeaders({'location': 'https://localhost/', 'set-cookie': 'Authorization="Bearer e
yJ0eXAiO5JKV1iLCJ4bGciOiJ2UzI1NiJ9.eyJzdWIiOi2b2Vqb2UiL253JleH1iOjE1DM0ODEzNTR7.zwbT9yV
OnV2V14Yrtuc1PP8wv82alz2354sgm0Rc7PgZIvc"; Domain=https://localhost; expires=Fri, 06 Mar 202
0 07:55:54 GMT; Max-Age=1800; Path=/'})

At BreakpointB(), on my test client side AFTER the response has been recieved, this is what response.headers and response.cookies looks like:

(Pdb) response.headers
{'content-length': '24956', 'content-type': 'text/html; charset=utf-8'}

(Pdb) response.cookies
<RequestsCookieJar[]>
(Pdb) response.cookies.get_dict()
{}

I have a strong suspicion this is because of the domain issue - but how do I correct for this? In my TestClient (Starlette TestClient), I have set client.base_url = 'https://localhost', and in my endpoint when making the cookie I've set DOMAIN=https://localhost. Anyone have any experience fixing this?

4 Answers

I experienced the same issue. While not a perfect solution, I ended up doing this in my test:

assert "Authorization" in response.headers['set-cookie']

...you could check for more things in the string as needed.

Just ran into this issue, in my case, it is indeed because of the cookie domain used in TestClient.

You can supply your cookie_domain as the base_url to your TestClient:

client = TestClient(app, base_url="http://example.com") 

set-cookie should work as long as the base_url is consistent with the cookie domain.

Edit: make sure your cookie domain is an FQDN without http:// or https:// prefixes

In my case the problem was that I set the max_age parameter of the cookie to float, instead of int. It was failing to parse the cookie because of that.

Hope that helps someone.

I was running into a similar problem and none of the above solutions worked for me. What I ended up doing just for testing purposes is modifying my cookie-setting code in my FastAPI server:

response.set_cookie(
  # ... other params
  domain=domain if domain != "testserver" else None
)

Then the TestClient responses had the cookies set as expected.

Related