编程 pytest 8.x 升 9.0/9.1:TOML 配置二选一、strict 连带开四项,autouse fixture 会跑两次

2026-10-29 00:03:39

pytest 8.x 升 9.0/9.1:TOML 配置、strict 总开关与几处静默改变结果的行为

信源:pytest CHANGELOG、9.0.0 release、9.1.0 release、backwards-compatibility 文档、issue #13893、discussion #13988。

下面按"升级时会实际碰到"的顺序记:先是配置层的变化,再是 strict 与 warning 语义,最后是 9.1 的两处收集/参数化回归。

原生 TOML 配置:两个表只能留一个

9.0 起 pyproject.toml 支持原生 TOML 类型的 [tool.pytest] 表(#13743),值不再被当成字符串:

[tool.pytest]
minversion = "9.0"
addopts = ["-ra", "-q"]
testpaths = ["tests", "integration"]

[tool.pytest.ini_options] 仍然可用,但两个表不能同时存在。也可以单独用 pytest.toml 或 .pytest.toml,表名写 [pytest]。

另一个更早生效的告警:配置分散在多个文件(比如 pytest.ini 与 pyproject.toml 里的 [tool.pytest.ini_options] 同时存在)现在会告警,说明其中只有一个生效(#13330)。升级后先跑一次 pytest --collect-only -q,把这条告警清掉再谈别的。

strict 变成总开关,锁版本再开

strict = true 会一次性打开四项(#13823):strict_config、strict_markers、strict_parametrization_ids、strict_xfail,单项可以显式覆盖:

[tool.pytest]
strict = true
strict_markers = false   # 单项回退

原先废弃的 --strict 命令行开关现在就是"打开 strict 模式"的别名。注意 CHANGELOG 里那句提醒:以后新增的 strict 选项也会被这个总开关自动带上,所以要开就先锁 pytest 版本(minversion/pytest==9.1.*)。

顺带一个 9.1 修的回归(#14442):9.0 里通过 addopts 指定的 --strict-markers/--strict-config 被静默忽略。也就是说,如果你的严格检查是靠 ini 里的 addopts 传的,在 9.0.x 上它可能根本没生效,看着是绿的。

PytestRemovedIn9Warning:9.0 报错,9.1 连类都没了

9.0 把 PytestRemovedIn9Warning 默认提升为 error(#13779),被它标记的功能在 9.1 真正移除。9.0.x 上临时降级:

[pytest]
filterwarnings =
    ignore::pytest.PytestRemovedIn9Warning

这行只在 9.0.x 有用。9.1 删掉了 PytestRemovedIn9Warning 类本身,过滤规则引用不存在的类不再直接失败,只输出一条提示(#13732),最终这些用例还是会在 9.1 上失败——降级只是给你争取一次发布窗口,不是迁移方案。

参数重叠去重:pytest a/ a/b 等价 pytest a

#12083 修的是"pytest a/ a/b 只跑 a/b",代价是行为变化:

pytest a/b a/     # 与 pytest a/ a/b 都等价于 pytest a
pytest x.py x.py  # 只跑一次,以前跑两遍

重叠的参数只保留前缀。依赖旧行为(重复执行、或想靠重复收集触发某些副作用)的用 --keep-duplicates 退回。

CI 判定与 Python 版本

$CI 或 $BUILD_NUMBER 必须定义为非空值才算 CI 模式(#13766)。CI="" 和 CI=0 之外的边界自己确认,空串不再是 CI:

CI=1 pytest
BUILD_NUMBER=42 pytest

Python 最低版本(#13719):9.0+ 要求 3.10+;8.4 是 3.9+;8.0–8.3 是 3.8+。另外 --version 单个开关不再加载插件基础设施(#13574),只作用于命令行,通过 PYTEST_ADDOPTS/addopts 设置无效。

9.0.2 / 9.0.3 的两个补丁

终端进度标签页(OSC 9;4)在 9.0 引入,#13896 从 9.0.2 起除 Windows 外默认关闭,要开得显式加载:

pytest -p terminalprogress

TERM=dumb 下不输出转义码。

9.0.3(2026-04-07)修了不安全临时目录的使用(CVE-2025-71176,#14343),这一条是不升 9.0.3 的硬理由。同一版还修了 approx 比较 Mapping 时考虑键顺序(#12444)、-p no: 屏蔽 conftest.py 明确禁止时报 UsageError(#13634)、exceptiongroup 带 __tracebackhide__ 的崩溃(#13734)。

9.1:--doctest-modules 下 autouse fixture 可能跑两次

这是 9.1 的 breaking(#14533):开启 --doctest-modules 时,定义在测试模块内联(不是插件、不是 conftest)的 autouse fixture,scope 为 module/package/session 时可能执行两次。

原因:模块可能被收集两次——一次作为 pytest.Module,一次作为 DoctestModule。fixture 实现改进后,DoctestModule 收集到的 fixture 只对它自己可见,两边各自注册,于是 setup 跑了两次。

修法是把 fixture 定义搬到 conftest.py,让两个收集器共享同一份可见性。副作用是 session/module 域的初始化逻辑(启动服务、建临时库)别再写进测试模块顶部。

同一版还有一批弃用,逐条扫一遍再动手:

  • #10819 类作用域 fixture 写成实例方法(无 @classmethod)告警,它会往另一个实例上设属性;
  • #12882 teardown 中 request.getfixturevalue() 请求未请求过的 fixture 弃用,10 变 error;
  • #13409 parametrize 的 argvalues 用生成器/迭代器弃用,值会被耗尽,导致重复 pytest.main()、类级 parametrize、多次收集时测试被跳过;
  • #13946 config.inicfg 弃用(9.0.2 用兼容垫片恢复过私有 config.inicfg,10 移除);
  • #14004 FixtureDef 传 baseid、fixture 注册 API 传 nodeid 字符串弃用,改用 node=(nodeid=None 改 node=session),10 移除;
  • #14335 用 marker 配置 hook(7.2 起弃用)10 移除;
  • #14434 --pastebin 弃用,改用 pytest-pastebin 插件;
  • #14513 FixtureDef.has_location 弃用;
  • #1764 pytest.console_main 弃用。

9.1.1 的两个回归修复

9.1.0 上线一周后补了两处(#14591、#14608):

  • 间接 parametrize 覆盖参数化 fixture 时,报 duplicate parametrization of ' ';
  • /test* 目录下的 conftest.py 未作为 initial conftest 加载,导致 pytest_addoption 不触发。

直接跳 9.1.1,别停在 9.1.0。

社区反馈:fixture 'mock_token_server' not found

discussion #13988 记录的是 8.3.5 → 9.0.0 后测试报:

fixture 'mock_token_server' not found

release notes 里没列这一条。维护者的回复与"fixtures 不再是普通函数"有关,建议把 responses.activate 这类装饰器放到 fixture 外层,而不是把装饰器套在 fixture 定义上。这条属于社区反馈,需自行验证;升级时若碰到同类 fixture not found,先检查 fixture 上是否叠了会改变函数签名的装饰器。

复制全文 生成海报 pytest Python 单元测试 CI Python测试

推荐文章

程序员茄子在线接单