pytest fixture 的 teardown 在 yield 后或 addfinalizer 注册的函数中执行,时机由 scope 决定:function 级每次测试后清理,class/session 级分别在类或会话结束时清理;teardown 异常默认静默,需主动捕获并验证资源是否释放。

pytest fixture 的 teardown 机制怎么触发
pytest fixture 默认没有自动清理逻辑,必须显式声明生命周期结束时的行为。关键不是“怎么写清理代码”,而是“什么时候执行它”——取决于 scope 和是否使用 yield 或 addfinalizer。
-
yield是最直观的方式:yield 前的代码是 setup,yield 后的代码就是 teardown,哪怕测试崩溃也会执行 -
addfinalizer更灵活,适合需要动态注册清理函数的场景(比如创建了临时文件但路径在运行时才确定) - 别用
return+finally:finally 块在 fixture 内部不会被 pytest 拦截为 teardown,测试异常时可能跳过
用 yield 写带清理的 fixture 最简实践
这是 90% 场景该选的方式。注意 yield 必须在函数体中出现,且只能 yield 一次;返回值传给测试函数,yield 后代码即为清理段。
import tempfile
import os
<p>@pytest.fixture
def temp_db_file():
fd, path = tempfile.mkstemp(suffix=".db")
os.close(fd)</p><h1>setup 完成,把路径交给测试</h1><pre class="brush:php;toolbar:false;">yield path
# teardown:无论测试成功或失败都会执行
if os.path.exists(path):
os.unlink(path)
- 测试函数里直接用
temp_db_file变量拿到文件路径 - 如果测试里抛出异常,
os.unlink(path)仍会执行 - 不要在 yield 后再 return —— yield 本身已隐含返回,多 return 会报
SyntaxError
fixture scope 影响清理时机,别乱设 scope="session"
scope 决定了 fixture 创建和销毁的频次,也直接决定清理动作多久执行一次。常见误用是把本该 per-test 清理的资源设成 session,导致测试间污染。
立即学习“Python免费学习笔记(深入)”;
-
scope="function"(默认):每个测试前后各执行一次 setup/teardown -
scope="class":整个测试类开始前 setup,结束后 teardown(适合类内多个测试共用一个 DB 连接) -
scope="session":整个 pytest 运行周期只 setup 一次、teardown 一次 —— 若清理逻辑依赖当前测试状态(比如删某个测试生成的 ID),这里就会出错 - 检查方法:加 print 日志,看 teardown 是否在预期时间点输出
清理失败时 pytest 不报错,得自己兜底
pytest 对 fixture teardown 中的异常默认静默吞掉,只在 -v 输出里提一句 “ERROR at teardown of …”,很容易被忽略。这意味着磁盘没清干净、端口没释放、数据库连接没 close,下次测试就可能失败。
- 在 teardown 段加 try/except + logging.warning,至少留痕
- 对关键资源(如端口、PID 文件),teardown 后主动验证是否真的释放:
assert not os.path.exists(path)或用socket尝试 connect - 避免在 teardown 里做耗时操作(如等待服务完全退出)——超时会导致整个测试 session 卡住
真正难的不是写清理逻辑,而是判断哪些状态必须清理、哪些可以依赖操作系统回收,以及在并行测试(pytest-xdist)下确保清理不互相干扰。


















