pytest测试调用asyncio协程的普通函数出现无事件循环错误如何解决
问题根因
- Mock路径错误:你需要Mock的是
sample模块中实际引用的asyncio.create_subprocess_shell,而不是全局asyncio库下的对应方法,这是单元测试Mock的通用规则,必须指向业务代码实际调用的导入路径。 - 模拟对象缺失必要属性:你自定义的
AsyncMock返回结果没有模拟子进程对象的returncode属性,sample_async访问该属性时触发异常,间接导致事件循环提前销毁,抛出无可用事件循环的错误。 - 额外优化点:你已经单独完成了
sample_async的单元测试,测试launcher时不需要真实执行异步协程逻辑,直接Mockasyncio.run可以完全避开事件循环相关问题。
解决方案
方案1:直接Mock asyncio.run(推荐)
该方案仅验证launcher的分支逻辑,不需要处理任何异步相关适配,性能最高且不会触发事件循环错误:
from unittest.mock import patch import sample def test_launcher_async_branch(): # Mock sample模块中调用的asyncio.run with patch("sample.asyncio.run") as mock_run: # 自定义协程执行返回值 mock_run.return_value = 0 res = sample.launcher("async", "/bin/bash ls") # 验证逻辑 mock_run.assert_called_once() assert res == 0 def test_launcher_sync_branch(): res = sample.launcher("no_asyncio", "") assert res == 0
方案2:保留真实异步执行逻辑
如果你需要验证sample_async和子进程调用的链路正确性,可修正Mock配置实现:
from unittest.mock import patch, MagicMock import sample class AsyncMock(MagicMock): async def __call__(self, *args, **kwargs): # 构造带returncode属性的模拟子进程对象 mock_proc = MagicMock() mock_proc.returncode = 0 return mock_proc # 修正patch路径为sample模块内的asyncio.create_subprocess_shell @patch("sample.asyncio.create_subprocess_shell", new_callable=AsyncMock) def test_launcher_full_link(mock_shell): res = sample.launcher("async", "/bin/bash ls") # 验证子进程调用参数 mock_shell.assert_called_once_with("/bin/bash ls") assert res == 0
内容的提问来源于stack exchange,提问作者SKP
相关产品推荐
相关产品推荐

