应使用适配异步模型的测试策略:一、pytest-asyncio插件自动管理事件循环;二、手动控制asyncio事件循环;三、unittest.isolatedasynciotestcase类;四、asyncmock模拟异步依赖;五、httpx与aresponses测试异步http。

如果您编写了 Python 异步函数(如使用 async def 定义的协程),但测试时出现挂起、超时或事件循环未正确管理等问题,则可能是由于测试框架未适配异步执行模型。以下是针对 Python 异步代码的多种测试策略:
一、使用 pytest-asyncio 插件运行协程测试
pytest-asyncio 提供了对 async/await 语法的原生支持,能自动管理事件循环并允许直接在测试函数中使用 await 表达式。
1、通过 pip 安装插件:pip install pytest-asyncio。
2、在测试文件或 conftest.py 中添加配置标记:pytest_plugins = "pytest_asyncio"。
立即学习“Python免费学习笔记(深入)”;
3、将测试函数声明为异步,并使用 @pytest.mark.asyncio 装饰器:async def test_fetch_data(): ...。
4、在函数体内直接 await 被测异步函数,无需手动创建或关闭事件循环。
二、手动管理 asyncio 事件循环进行单元测试
在不依赖第三方插件的情况下,可通过显式获取、运行和关闭事件循环来控制异步执行流程,适用于需要精确控制生命周期的场景。
1、在测试方法开头调用 asyncio.get_event_loop() 获取当前事件循环实例。
2、使用 loop.run_until_complete() 执行被测协程并获取返回值。
3、若需重置循环状态,调用 loop.close() 并在后续测试前重新创建新循环。
4、注意在 Python 3.12+ 中,get_event_loop() 已弃用,应改用 asyncio.new_event_loop() 并显式设置。
三、使用 unittest 的 AsyncTestCase 类
Python 3.8+ 内置的 unittest 框架提供了 AsyncTestCase 子类,专用于编写异步测试用例,其 setUp 和 tearDown 方法也支持异步定义。
1、继承 unittest.IsolatedAsyncioTestCase(推荐)或 unittest.AsyncTestCase(旧版别名)。
2、将测试方法定义为 async def test_xxx(self): 形式。
3、在方法内直接 await 被测协程及异步断言逻辑。
4、setUpAsync 和 tearDownAsync 方法可选定义,用于异步前置/后置操作。
四、模拟异步依赖以隔离测试边界
当异步函数依赖外部服务(如 HTTP 请求、数据库查询)时,需替换真实协程为可控的模拟对象,避免网络延迟与状态干扰。
1、使用 unittest.mock.AsyncMock 替代普通 Mock,使其支持 await 调用。
2、将 AsyncMock 实例赋值给被测对象的属性或作为参数传入,例如:mock_client.get = AsyncMock(return_value=response)。
3、在测试中 await 调用该模拟方法,并验证调用次数与参数:mock_client.get.assert_awaited_once_with("https://api.example.com")。
4、若需返回异常,设置 side_effect 为一个 Exception 实例或可 await 的异常抛出协程。
五、使用 httpx 或 aresponses 进行异步 HTTP 接口测试
对于涉及 HTTP 客户端调用的异步函数,需确保测试环境能拦截真实请求并返回预设响应,同时保持异步行为一致。
1、安装 httpx(支持异步客户端)和 aresponses(异步响应模拟库)。
2、在测试中创建 aresponses.Responses() 实例,并使用 add() 注册匹配规则与响应体。
3、使用 httpx.AsyncClient() 发起请求,确保所有调用均经由 aresponses 拦截。
4、验证响应内容与状态码,确认异步请求路径未触发实际网络 I/O。






