pytest 8.4.0 版本详解:RaisesGroup 异常组断言、收集控制与破坏性变更全解读
2026/9/15 14:56:25 网站建设 项目流程

pytest 8.4.0 版本详解:RaisesGroup 异常组断言、收集控制与破坏性变更全解读

【免费下载链接】pytestThe pytest framework makes it easy to write small tests, yet scales to support complex functional testing项目地址: https://gitcode.com/GitHub_Trending/py/pytest

pytest 8.4.0 于 2025-06-02 正式发布,是本仓库当前版本线中的一个重要里程碑:它正式支持 Python 3.14、引入用于断言ExceptionGrouppytest.RaisesGroup/pytest.RaisesExc、新增--force-short-summary--disable-plugin-autoload--stepwise-reset等命令行能力,同时移除对 Python 3.8 的支持。阅读本文后,你将掌握 8.4.0 的全部新特性、破坏性变更的迁移要点,以及这些功能在当前仓库中的源码级实现位置,可直接对照升级与使用。

版本总览与升级方式

pytest 8.4.0 是 8.x 系列中的功能型次版本,按照语义化版本规范(<major>.<minor>.<patch>,见 doc/en/changelog.rst),破坏性变更仅会在主版本中引入,因此 8.4.0 中的"移除与破坏性变更"属于提前预告的清理动作。官方发布公告见 doc/en/announce/release-8.4.0.rst,完整变更明细记录在 doc/en/changelog.rst。

从 PyPI 升级到最新版本只需一条命令:

pip install -U pytest

该版本包含新特性、既有功能改进与大量 bug 修复,累计收到 60 余位贡献者的提交。发布后,多个第三方插件(如 pytest-redis、pytest-elasticsearch、pytest-dynamodb、pytest-mcp 等)在 doc/en/reference/plugin_list.rst 中标注了pytest>=8.4.0的版本要求,说明本次发布成为社区插件基线升级的重要节点。

移除与破坏性变更:升级前必须注意的四点

8.4.0 的破坏性变更集中在四个方面,升级后部分"曾经只是警告"的行为会直接变成失败或错误。

异步测试不再"警告后跳过"

此前,若测试套件中存在异步测试但未安装任何合适的异步插件(如 pytest-asyncio、pytest-trio、pytest-twisted、pytest-anyio),pytest 只会发出警告并跳过;从 8.4.0 起,这类异步测试将直接失败(issue #11372)。这是为了让"异步测试没有正确运行"这一状态被显式暴露出来,而不是静默通过。

测试函数返回值非 None 将直接失败

测试函数若返回除None之外的任何值(这通常是忘记加断言、误把函数体写成表达式的结果),此前只会产生警告,8.4.0 起直接判为失败(issue #12346)。这是把"可疑的测试写法"从软提示升级为硬性错误,有助于尽早发现无效测试。

放弃 Python 3.8

随着 Python 3.8 于 2024-10-07 到达生命周期终点,8.4.0 正式放弃对它的支持(issue #12874)。如需继续使用 pytest 8.3.x 及更早版本,请保留 Python 3.8 环境;8.4.0 起请使用 Python 3.9+。

测试函数中残留的 yield 语法直接报错

在测试函数体内使用yield的写法(旧式生成器测试)自 pytest 4.0 起就已不再执行,此前以"预期失败 + 弃用警告"的形式提示;8.4.0 起直接产生显式错误(issue #12960),相关说明可参阅 doc/en/announce/../.. 系列的更早公告与 doc/en/historical-notes.rst 中的历史背景。

弃用预告:异步 fixture 的解析约定

8.4.0 对"同步测试请求异步 fixture"这一场景发出DeprecationWarning(issue #10839)。具体触发条件是:请求一个异步 fixture,但环境中没有任何提供pytest_fixture_setuphook 的实现来解析它。多数使用标准异步插件的用户不受影响,但非标准 hook 配置或autouse=True的用法需要留意。这为下一个主版本中的移除预留了缓冲期。

新特性详解

核心亮点:pytest.RaisesGroup 与 pytest.RaisesExc

8.4.0 最重要的功能是新增pytest.RaisesGroup,用于断言ExceptionGroup(Python 3.11+ 引入的异常组,或通过exceptiongroup回填包使用),其实现位于 src/_pytest/raises.py,类文档明确标注.. versionadded:: 8.4。同时新增pytest.RaisesExc(src/_pytest/raises.py),它现在就是pytest.raises的底层实现逻辑,也可作为RaisesGroup的参数来细化对子异常的匹配条件。

相比pytest.raises+ExceptionInfo.group_contains()RaisesGroup的匹配更严格:所有指定异常必须出现,且不允许出现任何多余异常。基本用法:

import pytest from pytest import RaisesGroup, RaisesExc def test_basic_group(): with RaisesGroup(ValueError): raise ExceptionGroup("", (ValueError(),)) def test_multiple_and_match(): with RaisesGroup( ValueError, ValueError, RaisesExc(TypeError, match="^expected int$"), match="^my group$", ): raise ExceptionGroup( "my group", [ValueError(), TypeError("expected int"), ValueError()], ) def test_nested_groups(): with RaisesGroup(RaisesGroup(ValueError)): raise ExceptionGroup("", (ExceptionGroup("", (ValueError(),)),))

关键参数说明(均可从 src/_pytest/raises.py 的 docstring 确认):

  • match:正则表达式,作用于异常组及其__notes__(PEP 678)的字符串表示;匹配前会去掉 repr 中" (5 subgroups)"之类的分组计数后缀。
  • check:回调函数,接收整个异常组,返回True才算匹配成功。
  • allow_unwrapped=True:当只期望单个异常/RaisesExc时,允许该异常不包裹在异常组中直接抛出;与matchcheck或多异常期望共用会报错。
  • flatten_subgroups=True:先把嵌套组内的所有异常"拍平"再匹配,适合模拟except*的行为。
  • 匹配不区分异常顺序,RaisesGroup(ValueError, TypeError)RaisesGroup(TypeError, ValueError)等价。

RaisesExc的三个匹配维度——异常类型、match正则、check回调——至少需要指定其一(src/_pytest/raises.py 会校验"三参皆空则抛ValueError")。RaisesExc(ValueError).matches(exc)还可以独立用于"逐个检查多个异常"的场景。仓库对应的类型检查与测试样例见 testing/typing_raises_group.py 与 testing/python/raises_group.py。

围绕raises系功能的配套增强还包括:

  • pytest.mark.xfailraises参数现在可以接收pytest.RaisesGroup(期望异常组时),也可以传pytest.RaisesExc以使用check参数(issue #12504)。
  • pytest.raises现在支持泛型写法pytest.raises(ExceptionGroup[Exception])以保留ExceptionInfo的完整类型标注(issue #13115)。
  • 传入空字符串给match会触发警告,因为空正则匹配一切;若想断言"异常没有任何消息",应使用match="^$"(issue #13192)。
  • 新增check=fn参数:fn接收被捕获的异常并返回布尔值,True视为匹配、False则重新抛出异常(issue #13192)。源码中check的 repr 与处理逻辑见 src/_pytest/raises.py。
  • match^...$包裹的转义字符串且匹配失败时,会输出可读的字符串 diff,方便定位差异(issue #13192)。

capteesys fixture:捕获的同时透传输出

新增capteesysfixture(issue #12081),其实现位于 src/_pytest/capture.py:它像capsys一样捕获 stdout/stderr,同时会把输出继续传递给--capture=指定的下一个处理器。适用于既需要断言输出内容、又希望输出正常流向日志/终端的场景:

def test_output(capteesys): print("hello") captured = capteesys.readouterr() assert captured.out == "hello\n" # 同时输出仍被透传到下一级处理器

--force-short-summary:强制紧凑摘要

新增--force-short-summary选项(issue #12713,CLI 定义见 src/_pytest/terminal.py),无论当前 verbosity 级别多高,都强制使用精简摘要输出失败信息。这在 CI 日志、任务输出中快速定位失败尤其有用——当非紧凑输出非常冗长时,仍然能看到一行行的失败摘要。

collect_imported_tests:控制跨文件收集

pytest 传统上会收集测试模块命名空间中从其他文件 import 进来的类与函数。例如在tests/test_testament.pyfrom domain import Testament,由于TestamentTest开头,默认情况下它会被当作测试类收集。8.4.0 新增配置项collect_imported_tests(issue #12749,ini 注册见 src/_pytest/main.py,收集逻辑见 src/_pytest/python.py),设为其默认值true之外的false时,pytest 只收集定义在本文件内的测试类/函数:

# pytest.ini [pytest] collect_imported_tests = false
# contents of tests/test_testament.py from domain import Testament # 不会再被收集 def test_testament(): ... # 仍会被收集

配套测试见 testing/test_collect_imported_tests.py。

断言截断阈值可配置

新增truncation_limit_linestruncation_limit_chars两个配置项(issue #12765),用于控制断言失败信息中代码片段与差异的截断阈值。其实现位于 src/_pytest/assertion/truncate.py,max_lines/max_chars直接读自这两个 ini 项,并在 src/_pytest/assertion/init.py 注册:

[pytest] truncation_limit_lines = 30 truncation_limit_chars = 800

console_output_style 支持 times

console_output_style新增times取值(issue #13125),可在每条测试结果后展示其执行耗时,适合做性能摸底:

pytest --console-output-style=times

--disable-plugin-autoload:替代环境变量的命令行开关

此前要禁用插件自动加载需设置PYTEST_DISABLE_PLUGIN_AUTOLOAD环境变量;8.4.0 新增等价的--disable-plugin-autoload标志(issue #13253,见 src/_pytest/helpconfig.py),并且由于它可以写进addopts,现在还能在配置文件中统一控制:

[pytest] addopts = --disable-plugin-autoload

隐藏参数集:id 中不显示参数

hidden-param占位符(\0)此前用于隐藏参数集,现在可直接用在pytest.paramidMetafunc.parametrizeids中(issue #13228),隐藏该参数集在测试名中的显示。

既有功能改进

PEP 657 追踪:traceback 显示精确表达式位置

shortlong两种 traceback 风格现在获得部分 PEP 657 支持(issue #10224),能够像 Python 3.11+ 的调试体验一样,用^精确标出出错的那段表达式:

test_tracebacks.py:12: in test_gets_correct_tracebacks assert manhattan_distance(p1, p2) == 1 ^^^^^^^^^^^^^^^^^^^^^^^^^^ test_tracebacks.py:6: in manhattan_distance return abs(point_1.x - point_2.x) + abs(point_1.y - point_2.y) ^^^^^^^^^ E AttributeError: 'NoneType' object has no attribute 'x'

pythonpath 更早生效

pythonpath配置项现在会在初始化更早的阶段写入$PYTHONPATH(issue #11118),因此也影响通过-p选项加载的插件——自定义插件若要依赖测试目录下的模块,这一改动直接解决了加载顺序问题。

parser.addini 支持 int/float 类型

插件作者在pytest_addoption中调用parser.addini时,type参数新增"int""float"支持(issue #11381),让配置文件中的数值自动完成类型解析:

def pytest_addoption(parser): parser.addini("int_value", type="int", default=2, help="my int value") parser.addini("float_value", type="float", default=4.2, help="my float value")
[pytest] int_value = 3 float_value = 5.4

fixture 显示为 "fixture object"

测试输出中 fixture 现在以明确的 "fixture object" 形式呈现,而不是普通的函数对象(issue #11525),初学者更容易发现"在同一模块声明了 fixture 却忘了在测试函数参数中请求它"这类错误。

其他值得关注的改进

  • JUnit XML:根标签testsuites新增固定值属性name="pytest tests"(issue #12736),符合 junit-10.xsd 规范(schema 见 testing/junit-10.xsd)。
  • unraisable 与 thread exception 全面增强(issue #12958 / #13016):尽早挂载 hook、在卸载前调用 GC、每个测试阶段收集多条异常、报告tracemalloc分配回溯、避免基于生成器的 hook 以正确处理StopIteration、把未处理异常作为警告的 cause,并在 hook 内即时计算repr防止对象被复活导致信息失真。
  • pytest.approx 改进:支持"数字与非数字混合的集合"比较(issue #13010);修复boolnumpy.bool_的相等性(issue #13047,8.3.4/8.3.5 引入的回归);repr在 0.001~1000 区间内以十进制而非科学计数法展示容差,如42 ± 1(issue #6985);并补充说明approx认为布尔值与数字 0/1 不相等(issue #13218)。实现入口见 src/_pytest/approx.py。
  • stepwise 模式重大改进(issue #13122,实现见 src/_pytest/stepwise.py):不再"忘记"上次失败的测试——即使后续直接运行不带--stepwise的隔离测试,再次--stepwise时也会从上次失败处继续;测试套件变化(当前按测试数量判断)会自动重置内部状态;新增--stepwise-reset/--sw-reset显式清空状态重启工作流。
  • Python 3.14 官方支持(issue #13308)。
  • 异常组 traceback 过滤:过滤ExceptionGroup回溯时排除 pytest 内部帧(issue #13380)。
  • 收集性能优化:优化FSCollector的路径解析,并给nodes._check_initialpaths_for_relpathlru_cache(issue #13420)。
  • 错误信息更友好:重复参数化错误不再展示内部堆栈(issue #13457);空usefixtures标记发出警告(issue #12426);在pytest.param上使用usefixtures由静默无效改为报错(issue #4112);断言重写警告信息中的:改为;以便用标准 warning 过滤器处理(issue #5473)。
  • 输出渲染pygments由可选依赖变为必选依赖,输出始终带源码高亮,可用--code-highlight=no关闭(issue #7683)。
  • Pdb:Python 3.13+ 下可以在 Pdb 中导航异常链(issue #12707)。

值得关注的 bug 修复要点

  • --durations-min-vv下不再失效(issue #12938)。
  • 测试、setup、teardown 中抛出的StopIteration得到正确处理(issue #12929)。
  • 修复@pytest.mark.parametrize等标记位于@staticmethod/@classmethod之上时未被应用的问题(issue #12863)。
  • Metafunc.parametrizeindirect=True时传scope不再破坏其他 fixture 对参数化 fixture 的依赖(issue #13248)。
  • 修复支持位置只读self/ 关键字只读 fixture 参数的方法定义,如def test_method(self, /, *, fixture): ...(issue #13377)。
  • 修复 pytest 可能报告负耗时的异常(issue #13384)。
  • 修复 PyPy 上收集高阶作用域参数时可能的KeyError崩溃(issue #13312)。
  • Config.add_cleanup回调抛异常不再阻断后续 cleanup(issue #12981)。
  • filterwarnings 的应用与撤销时机提前/延后,使"警告即错误"能覆盖整个运行过程,包括卸载 unraisable/threadexcept hook 之前(issue #10404)。

打包与下游说明

  • 明确指定coloramainiconfigpackaging的最低允许版本,并将python_version<'3.11'exceptiongroup的最低版本从 RC 提升为正式版(issue #13317)。
  • pytest.TerminalReporter被纳入公开 API,因为它是pytest_terminal_summaryhook 签名的一部分(issue #6649),文档见 doc/en/reference/reference.rst。
  • --help输出中的 CLI 选项分组得到整理(issue #13221)。

如何在当前仓库验证与深入阅读

本文所依据的完整变更记录位于 doc/en/changelog.rst(pytest 8.4.0 章节),发布公告见 doc/en/announce/release-8.4.0.rst。若想深入源码:

  • 异常组断言实现:src/_pytest/raises.py(RaisesGroup)与 src/_pytest/raises.py(RaisesExc),测试见 testing/python/raises_group.py;
  • stepwise 状态管理与重置:src/_pytest/stepwise.py;
  • 输出捕获透传:src/_pytest/capture.py;
  • 收集控制与截断配置:src/_pytest/python.py、src/_pytest/assertion/truncate.py;
  • 插件自动加载开关:src/_pytest/helpconfig.py。

升级到 8.4.0 后,建议优先自查四类破坏性变更(异步测试、非 None 返回值、Python 3.8、函数内 yield),再逐步引入RaisesGroup--force-short-summary等新能力,即可平稳完成本次版本迁移。

【免费下载链接】pytestThe pytest framework makes it easy to write small tests, yet scales to support complex functional testing项目地址: https://gitcode.com/GitHub_Trending/py/pytest

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询