pytest测试async context manager fixture返回意外async_generator对象
在pytest fixture中使用异步上下文管理器返回async_generator而非实例的解决办法
问题重现
定义异步上下文管理器类后,在pytest fixture中通过async with+yield返回实例,但测试函数中拿到的是async_generator对象,调用方法时报错:
AttributeError: 'async_generator' object has no attribute 'add_numbers'
相关代码:
import asyncio import pytest class MyAsyncContextManager: async def __aenter__(self): print("Entering async context") return self async def __aexit__(self, exc_type, exc, tb): print("Exiting async context") def add_numbers(self, a, b): return a + b @pytest.fixture async def async_context_manager(): async with MyAsyncContextManager() as manager: yield manager @pytest.mark.asyncio async def test_async_context_manager(async_context_manager): async_context_manager.add_numbers(1, 2)
原因分析
使用普通的@pytest.fixture装饰带yield的异步函数时,pytest不会自动解析异步生成器的产出值,而是直接将生成器对象注入测试函数,导致无法直接调用实例方法。
解决方法
方法1:使用pytest-asyncio的专用fixture装饰器
先安装pytest-asyncio(如果未安装):
pip install pytest-asyncio
将fixture的装饰器替换为@pytest_asyncio.fixture,pytest会自动处理异步生成器,把yield的实例注入测试函数:
import pytest_asyncio @pytest_asyncio.fixture async def async_context_manager(): async with MyAsyncContextManager() as manager: yield manager @pytest.mark.asyncio async def test_async_context_manager(async_context_manager): assert async_context_manager.add_numbers(1, 2) == 3
方法2:手动迭代异步生成器获取实例
如果不想依赖pytest_asyncio的专用装饰器,可在测试函数中通过await anext()手动获取异步生成器的产出值:
@pytest.mark.asyncio async def test_async_context_manager(async_context_manager): manager = await anext(async_context_manager) assert manager.add_numbers(1, 2) == 3
方法3:fixture返回上下文管理器实例,测试中手动管理上下文
将fixture改为直接返回上下文管理器实例,在测试函数中通过async with自行控制上下文的进入和退出:
@pytest.fixture def async_context_manager(): return MyAsyncContextManager() @pytest.mark.asyncio async def test_async_context_manager(async_context_manager): async with async_context_manager as manager: assert manager.add_numbers(1, 2) == 3
内容的提问来源于stack exchange,提问作者Kumar Gaurav
相关产品推荐
相关产品推荐

