FastAPI中pytest使用httpx AsyncClient异步测试的实现及报错解决
FastAPI 异步测试双报错解决方案
报错核心根因
RuntimeError: This event loop is already running:同步版TestClient内部逻辑会独立绑定/创建事件循环,和pytest-asyncio给异步测试用例分配的单例事件循环产生绑定冲突,属于客户端和测试框架的兼容逻辑问题,不是测试用例本身写错。httpx.ConnectError: [Errno 8] nodename nor servname provided, or not known:旧版本httpx中,直接给AsyncClient传app=app参数时,不会自动切换到ASGI内存调用模式,反而会按照配置的base_url发起真实网络请求,在macOS等环境下解析0.0.0.0地址时会触发DNS解析失败,请求根本没走到本地的FastAPI应用逻辑。
可落地实现步骤
1. 对齐依赖版本避免兼容坑
- 安装0.20及以上版本的httpx,该版本正式支持
ASGITransport内存调用模式,无需发起真实网络请求即可测试ASGI应用 - 匹配安装和当前pytest版本兼容的
pytest-asyncio,避免异步测试执行逻辑异常
2. 修正异步客户端fixture写法
摒弃直接给AsyncClient传app=app的旧写法,改用官方推荐的ASGITransport绑定应用,完全在测试进程内存中处理请求,不会触发真实网络请求,也不会和pytest的事件循环产生冲突。
正确的fixture实现代码:
import pytest_asyncio from httpx import AsyncClient, ASGITransport from 你的项目路径.main import app # 替换为实际的FastAPI应用导入路径 @pytest_asyncio.fixture(scope="module") async def async_client(): transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://testserver") as client: yield client
3. 修正测试用例的细节问题
- 移除Python单元测试自带的
assertEqual导入逻辑,直接使用pytest原生断言语法即可,避免导入冲突 - 原有针对内部依赖的mock逻辑可以完整保留,
ASGITransport的内存调用模式不会影响依赖mock的生效
修正后的测试用例示例:
@pytest.mark.asyncio @mock.patch('apps.core.views.requests.get', new=mocked_get_request) async def test_get_non_json_response(async_client: AsyncClient): response = await async_client.get("/mymedia") assert response.json()['error']['message']['message'] == 'Not json'
额外避坑提示
- 不要在异步测试流程中混用同步
TestClient的调用逻辑,否则一定会触发事件循环重复绑定的报错 - 如果路由依赖Redis等外部组件,需要在测试场景下通过FastAPI的依赖注入逻辑替换为测试用mock实例,不要让测试流程真的连接外部生产服务
- 如果遇到模块级fixture的事件循环复用问题,可在
pytest.ini配置文件中添加asyncio_mode = auto,匹配模块级fixture的事件循环作用域,避免循环重建异常
内容的提问来源于stack exchange,提问作者Marc LaBelle
相关产品推荐
相关产品推荐

