使用pytest测试异步方法时遇到TypeError: 'coroutine' object does not support the asynchronous context manager protocol错误
pytest测试异步方法时遇到TypeError: 'coroutine' object does not support the asynchronous context manager protocol错误
看起来你遇到的问题核心是异步上下文管理器的模拟方式不对,我帮你分析下问题所在,再给出修复方案:
问题根源
你在mock_stream_response fixture里,用普通lambda来定义__aenter__和__aexit__,但异步上下文管理器要求这两个方法必须是异步可调用的——也就是它们要么是用async def定义的函数(返回协程对象),要么是能返回awaitable的对象。
你用普通lambda返回的是普通值(比如直接返回response),这不符合异步上下文管理器的协议要求,当async with尝试调用这些方法时,就会把返回的对象误当作协程处理,触发类型错误。另外,__aexit__的签名也不对,它必须接受四个参数:self, exc_type, exc_val, exc_tb,你的lambda没处理这些参数,也会导致潜在问题。
修复方案
我给你两种修复方式,选一种就行:
方式1:用async def定义合法的异步方法
直接用标准的异步函数来实现__aenter__和__aexit__,确保它们符合协议要求:
@pytest.fixture async def mock_stream_response( response: AsyncMock, ): class TestMockResponse: file: ClassVar[bytes] = b'test' headers: ClassVar[dict[str, str]] = {"content-type": "application/pdf"} mock = AsyncMock() mock.raise_for_status.return_value = MagicMock(status=200) # 异步实现__aenter__,返回你预设的response async def async_enter(self): return response mock.__aenter__ = async_enter # 异步实现__aexit__,符合标准签名 async def async_exit(self, exc_type, exc_val, exc_tb): return None mock.__aexit__ = async_exit mock.aread.return_value = TestMockResponse.file yield mock mock.reset_mock()
方式2:用AsyncMock自动处理异步逻辑(更简洁)
既然你已经在用AsyncMock,可以直接利用它的特性来模拟这两个方法,不用自己写异步函数:
@pytest.fixture async def mock_stream_response( response: AsyncMock, ): class TestMockResponse: file: ClassVar[bytes] = b'test' headers: ClassVar[dict[str, str]] = {"content-type": "application/pdf"} mock = AsyncMock() mock.raise_for_status.return_value = MagicMock(status=200) # 直接设置__aenter__的返回值,AsyncMock会自动处理成异步调用 mock.__aenter__.return_value = response # __aexit__用AsyncMock默认实现,或者显式设置返回值 mock.__aexit__.return_value = AsyncMock(return_value=None) mock.aread.return_value = TestMockResponse.file yield mock mock.reset_mock()
额外的小建议
看你response fixture里的这段代码:
response.headers.return_value = MagicMock(return_value=TestMockResponse.response_headers)
真实的httpx.Response里headers是属性,不是可调用的方法,所以mock的时候应该直接赋值属性,而不是mock方法:
# 替换成直接赋值 response.headers = TestMockResponse.response_headers
这样更贴合真实的Response结构,避免测试中出现不必要的错误。
按照上面的修改,应该就能解决你遇到的类型错误了,如果还有其他问题可以再补充细节~
备注:内容来源于stack exchange,提问作者haku
相关产品推荐
相关产品推荐

