pytest集成测试需显式管理fixture生命周期:async fixture会报valueerror;db连接用scope="session";http服务fixture需autouse=true和yield;tmp_path比tempfile更安全;assert要检查异常链;参数化测试需indirect=true控制顺序。

pytest集成测试必须显式管理fixture生命周期
Python 3.11本身不改变pytest行为,但async/await支持更成熟,容易误用async def写fixture——这会导致pytest直接报错ValueError: fixture function cannot be a coroutine。集成测试常需连接数据库、启动HTTP服务等耗时操作,必须用scope="session"或scope="module"控制复用,否则每次test都重连,既慢又可能触发资源冲突。
实操建议:
- 用
@pytest.fixture(scope="session")封装DB连接池初始化,返回连接对象而非每次新建 - 避免在fixture里调用
asyncio.run()——它会创建新event loop,与pytest主线程loop冲突;改用asyncio.get_event_loop().run_until_complete()(需确保loop已存在) - HTTP服务类fixture(如用
httpx或requests测本地FastAPI)务必加autouse=True和yield,确保服务启停配对
用tmp_path比tempfile更安全地处理临时文件
集成测试常要读写配置、日志、上传文件等,Python 3.11的tempfile模块仍存在跨平台路径问题(尤其Windows长路径),而pytest内置的tmp_path fixture自动适配OS、自动清理、线程安全。
常见错误:手动拼接os.path.join(tempfile.gettempdir(), "mytest.db")——测试并行运行时可能冲突,且pytest无法保证退出时删除。
实操建议:
- 直接声明
def test_something(tmp_path):,所有临时文件存到tmp_path / "config.yaml" - 若需子目录,用
(tmp_path / "data").mkdir(),别用os.makedirs() - 不要把
tmp_path传给子进程——它的路径在子进程中不可见;改用tmp_path.resolve()转绝对路径再传
assert语句要覆盖异常链和上下文
集成测试失败时,往往不是断言值错,而是上游抛了异常却没被捕获。Python 3.11增强的异常链(__cause__和__context__)如果被忽略,会导致错误信息丢失,比如DB连接失败后HTTP handler抛500 Internal Server Error,但真正原因是psycopg2.OperationalError。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
实操建议:
- 用
with pytest.raises(ExpectedError) as excinfo:捕获异常,再检查excinfo.value.__cause__ - 对HTTP响应,别只断言
resp.status_code == 200,加assert "detail" not in resp.json()防未处理异常泄露 - 日志断言用
caplog.records而不是caplog.text,前者能访问.levelname、.exc_info等结构化字段
参数化测试要注意fixture依赖顺序
当用@pytest.mark.parametrize驱动多组输入,并同时依赖多个fixture(如db、client、tmp_path)时,pytest按fixture声明顺序注入参数,但若某fixture内部用了request.param,就可能因执行时机错乱导致FixtureLookupError。
典型场景:想为不同DB方言(sqlite/postgres)跑同一组API测试,但db fixture根据request.param切换引擎,而client fixture又依赖db——此时client会在db初始化前就被请求。
实操建议:
- 把参数化逻辑下沉到fixture内部,而非在test函数上标
@pytest.mark.parametrize - 用
indirect=True强制让参数名匹配fixture名,例如@pytest.mark.parametrize("db", ["sqlite", "postgres"], indirect=True) - 避免在fixture里读
request.param后再调用另一个fixture——改用request.getfixturevalue("other_fixture")显式获取,确保执行顺序可控
最易被忽略的是异步fixture和同步测试混用:Python 3.11允许async def test_*,但pytest默认不支持,需装pytest-asyncio并显式标记@pytest.mark.asyncio,否则测试会静默跳过而非报错。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










