I'm testing a FastAPI application with asynchronous endpoints using pytest and httpx.AsyncClient. I have an endpoint /logout that depends on a JWT token from cookies and uses some async dependencies.
Here is my test:
import pytest
from httpx import ASGITransport, AsyncClient
from unittest.mock import AsyncMock
from app.main import app
@pytest.mark.asyncio
async def test_auth_callback_creates_user(monkeypatch):
mock_user = AsyncMock()
mock_user.id = 123
async def mock_authorize_access_token(request):
return {"userinfo": {"email": "[email protected]", "given_name": "Test", "sub": "xyz"}}
monkeypatch.setattr("app.routers.view.core_views.google.authorize_access_token", mock_authorize_access_token)
monkeypatch.setattr("app.routers.view.core_views.get_user_email", AsyncMock(return_value=None))
monkeypatch.setattr("app.routers.view.core_views.add_user", AsyncMock(return_value=mock_user))
monkeypatch.setattr("app.routers.view.core_views.encode_jwt", lambda payload: "fake-jwt")
monkeypatch.setattr("app.routers.view.core_views.save_jwt_token", AsyncMock())
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://test"
) as ac:
resp = await ac.get("/auth/callback")
assert resp.status_code == 302
assert resp.headers["location"] == "/profile-view/"
assert "access_token" in resp.cookies
@pytest.mark.asyncio
async def test_user_fixture(monkeypatch):
mock_user = AsyncMock()
mock_user.id = 123
assert mock_user.id is not None
@pytest.mark.asyncio
async def test_logout_removes_cookie(monkeypatch):
mock_payload = {"sub": "1"}
monkeypatch.setattr("app.routers.view.core_views.get_current_token_payload", AsyncMock(return_value=mock_payload))
monkeypatch.setattr("app.routers.view.core_views.blacklist_token", AsyncMock())
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://test"
) as ac:
resp = await ac.get("/logout", cookies={"access_token": "fake-token"})
assert resp.status_code == 302
assert resp.headers["location"] == "/authentication"
command: poetry run pytest -v tests/test_user_oauth.py
requirements.txt: (necessary)
fastapi==0.112.4
httpx==0.28.1
pytest==8.3.4
pytest-asyncio==1.0.0
pytest-mock==3.14.1
SQLAlchemy==2.0.36
asyncpg==0.30.0
alembic==1.14.0
Authlib==1.4.0
python-dotenv==1.0.1
redis==6.2.0
PyJWT==2.10.1
I want to test that /logout returns a 302 redirect and removes the cookie. But instead I get an error. FAILED tests/test_user_oauth.py::test_logout_removes_cookie - RuntimeError: Event loop is closed
What I've tried:
- Putting the ASGITransport into pytest fixtures
- Explicitly closing the client with await ac.aclose() at the end of the test.
- Switching pytest.mark.asyncio to pytest.mark.anyio with anyio_backend = asyncio.
- looked at other posts on this topic.