如何在Pytest中实现异步的Setup和Teardown夹具
如何在Pytest中实现异步的Setup和Teardown夹具
嘿,这个问题我之前踩过一模一样的坑!其实核心就是没摸透Pytest处理异步fixture的正确姿势,我给你一步步捋清楚怎么解决:
首先先纠正你几个关键的错误操作:
- 你完全不需要在测试函数里手动
await注入的fixture变量,Pytest会自动帮你执行异步fixture的逻辑,直接拿到yield出来的值。 - 出现
async_generator对象的问题,大概率是你的测试环境没配置对,或者测试函数本身不是异步的。
接下来是正确的实现步骤,照着做就没问题:
1. 先把环境配置好
虽然新版Pytest原生支持异步,但pytest-asyncio插件能帮我们处理好异步运行环境的细节,避免踩坑,先装一下:
pip install pytest pytest-asyncio
2. 正确定义异步Fixture
异步fixture的写法和同步的几乎一样,只是把函数改成async def,然后在setup和teardown部分用await调用异步操作就行,还是用普通的@pytest.fixture装饰器:
import pytest import asyncio # 模拟你的异步setup函数 async def my_setup_func(): await asyncio.sleep(0.1) print("异步setup执行完成") # 模拟你的异步teardown函数 async def my_teardown_func(): await asyncio.sleep(0.1) print("异步teardown执行完成") @pytest.fixture async def async_setup_teardown(): # 异步Setup逻辑 print("开始执行setup") await my_setup_func() # 把测试需要的值yield出去 yield "test_variable" # 异步Teardown逻辑,测试结束后自动执行 print("开始执行teardown") await my_teardown_func()
3. 正确编写异步测试函数
测试函数必须是async def,然后直接把fixture作为参数注入,不需要手动await,Pytest会自动帮你跑通fixture的异步流程,把yield出来的值直接给你:
async def test_async_example(async_setup_teardown): # 直接用注入的变量,就是fixture里yield的"test_variable" test_var = async_setup_teardown print(f"从fixture拿到的值: {test_var}") # 执行你的异步测试逻辑 result = await my_async_test_func(test_var) # 断言结果 assert result == "test_variable_processed" # 模拟你的异步测试函数 async def my_async_test_func(input_val): await asyncio.sleep(0.1) return f"{input_val}_processed"
为什么你之前会出错?
- 你在测试里写
variable = await db_data_setup_and_teardown是完全错误的:Pytest已经帮你处理了异步fixture的执行,注入到测试函数里的是yield出来的最终值,不是异步生成器对象,所以根本不需要手动await。 - 如果你拿到的是
async_generator对象,要么是你的Pytest版本太低(建议升级到7.0+),要么是测试函数不是async def,导致Pytest没按异步方式处理fixture。
额外小提示
- 异步fixture也支持作用域(比如
@pytest.fixture(scope="module")),和同步fixture用法完全一样,作用域内的测试会共享这个fixture的setup和teardown。 - yield之后的teardown逻辑,不管测试成功还是失败都会自动执行,和同步fixture的行为一致,不用担心资源泄漏。
这样改完之后,你的异步setup和teardown就能正常工作啦!




