You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 15:20:29