应使用 @pytest.fixture 而非 setup_function/teardown_method;yield 实现前置+后置原子性保障,确保清理代码必执行;默认 scope="function" 保障用例独立;conftest.py 是共享 fixture 的合理出口;后置需异常处理且避免关键断言。

直接用 @pytest.fixture,别写 setup_function 或 teardown_method —— 这是当前最可靠、最易维护的前后置方式。
为什么 yield 比 return 更适合前后置
yield 是 fixture 实现“前置 + 后置”原子性保障的关键。它把函数逻辑一分为二:yield 前执行准备,yield 后执行清理,且后置部分一定会运行(除非 fixture 自身初始化就崩溃)。
- 测试断言失败、抛出异常、甚至
sys.exit(),yield后的代码仍会执行 - 用
return则无法自动触发后置;你得自己 try/finally 包裹,容易漏或写错 -
yield只能出现一次,强制你把资源生命周期理清楚 —— 多个 yield 会报ValueError: more than one 'yield'
示例:
@pytest.fixture
def db_conn():
conn = create_db_connection() # 前置
yield conn
conn.close() # 后置:这里永远会跑
scope="function" 是默认安全区,别轻易改
fixture 默认作用域是 function,意味着每个测试函数都拿到一个全新实例。这是保障用例独立性的底线。
-
scope="class":类内所有测试共享一个 fixture 实例 → 若某个测试修改了该实例状态(如清空了缓存、重置了 token),后续测试可能失效 -
scope="module":整个文件只初始化/清理一次 → 适合数据库连接池、配置加载等开销大但状态无副作用的操作 -
scope="session":整个 pytest 运行周期只执行一次 → 仅适用于全局只读资源(如预编译的 schema、固定 mock server 地址),绝不能用于任何可变状态
误用高 scope 最常见的现象是:单测通过,但多个用例一起跑时偶发失败,且难以复现。
conftest.py 是共享 fixture 的唯一合理出口
把通用 fixture 放进 conftest.py,比在每个测试文件里重复定义强十倍。pytest 会自动向上查找,无需 import。
- 同一目录下所有
test_*.py都能直接使用conftest.py里的 fixture - 子目录的
conftest.py可覆盖父级定义,适合分层定制(如 API 测试目录用api_session,DB 测试目录用db_transaction) - 避免在 fixture 里硬编码路径或环境变量 —— 应通过
pytest config或os.getenv()动态获取,否则conftest.py一挪就挂
典型结构:
# conftest.py
import pytest
import requests
<p>@pytest.fixture
def api_session():
s = requests.Session()
s.headers.update({"Authorization": "Bearer test-token"})
yield s
s.close() # 自动关闭,不依赖测试是否成功</p>
复杂清理逻辑必须显式处理异常
fixture 后置代码虽“一定执行”,但若清理过程本身出错(比如文件已被删、连接已断),会掩盖原始测试失败,导致日志混乱。
- 在
yield后加try/except捕获清理异常,并用logging.warning记录,而不是让其冒泡 - 不要在后置里做关键断言(如“必须删除临时目录”)—— 清理失败不该让测试变红,它只是收尾动作
- 涉及进程、端口、锁文件等 OS 级资源时,建议用
atexit.register()做兜底,因为 fixture 后置在 pytest 异常退出时可能来不及运行
容易被忽略的一点:yield 返回的对象如果被测试函数修改(比如往 dict 里塞字段、调用对象方法改变内部状态),这些变更会延续到下一个同 scope 的测试中 —— 这不是 bug,是 Python 对象引用的自然行为,需主动深拷贝或重建。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











