弃用和移除

此页面列出了所有当前已弃用或已在过去主要版本中移除的 pytest 功能。目的是向用户清晰地说明某个功能被移除的原因,以及应使用哪些替代方案。

已弃用的功能

以下是所有被视为已弃用的 pytest 功能的完整列表。使用这些功能将发出 PytestWarning 或其子类,可以使用 标准警告过滤器 进行过滤。

monkeypatch.syspath_prepend 与传统命名空间包

自 9.0 版本弃用。

当使用 monkeypatch.syspath_prepend() 时,如果导入了 pkg_resources,pytest 会自动调用 pkg_resources.fixup_namespace_packages()。这仅适用于使用 pkg_resources.declare_namespace() 的传统命名空间包。

传统命名空间包已弃用,取而代之的是原生命名空间包(PEP 420)。如果您在 __init__.py 文件中使用 pkg_resources.declare_namespace(),您应该通过从命名空间包中移除 __init__.py 文件来迁移到原生命名空间包。

此弃用警告仅在以下情况发出:

  1. pkg_resources 已导入,并且

  2. 被预置的特定路径包含一个已声明的命名空间包(通过 pkg_resources.declare_namespace())。

要解决此警告,请将您的传统命名空间包转换为原生命名空间包

传统命名空间包(已弃用)

# mypkg/__init__.py
__import__("pkg_resources").declare_namespace(__name__)

原生命名空间包(推荐)

只需完全移除 __init__.py 文件即可。Python 3.3+ 原生支持不带 __init__.py 的命名空间包。

依赖于异步 fixture 的同步测试

自 8.4 版本弃用。

长期以来,pytest 在遇到异步测试函数时会报错,提示用户安装一个可以处理它的插件。但是,如果有一个异步 fixture 被同步测试所依赖,则不会报错。如果 fixture 是一个异步函数,你会收到“未等待的协程”警告,但对于异步 yield fixture,你甚至不会收到。即使你安装了处理异步测试的插件,这也是一个问题,因为它们可能需要特殊的装饰器来处理异步 fixture,并且有些可能无法健壮地处理用户意外地从同步测试请求异步 fixture 的情况。fixture 值被缓存会使这更加难以理解,如果 fixture 首先被异步测试请求,然后被同步测试请求,一切都将“正常工作”。

不幸的是,没有 100% 可靠的方法来识别用户何时犯了错误,或者何时他们期望从 fixture 中获得一个未等待的对象,并由他们自己处理。要在确实打算处理这种情况时抑制此警告,您可以将异步 fixture 包装在同步 fixture 中

import asyncio
import pytest


@pytest.fixture
async def unawaited_fixture():
    return 1


def test_foo(unawaited_fixture):
    assert 1 == asyncio.run(unawaited_fixture)

应更改为

import asyncio
import pytest


@pytest.fixture
def unawaited_fixture():
    async def inner_fixture():
        return 1

    return inner_fixture()


def test_foo(unawaited_fixture):
    assert 1 == asyncio.run(unawaited_fixture)

您还可以利用 pytest_fixture_setup 在 pytest 看到协程/异步生成器之前处理它们——这是当前异步 pytest 插件的处理方式。

如果用户在 conftest.py 中或在包含同步测试和 fixture 的文件中有一个带有 autouse=True 的异步 fixture,他们将收到此警告。除非您正在使用专门处理带有同步测试的异步 fixture 的插件,否则我们强烈建议不要采用这种做法。这可能导致不可预测的行为(对于更大的作用域,如果异步测试首先请求 fixture,由于值缓存,它可能看起来“正常工作”),并会生成未等待协程的运行时警告(但仅适用于非 yield fixture)。此外,它会给其他开发人员造成歧义,即 fixture 是否旨在为同步测试执行设置。

anyio pytest 插件支持带有异步 fixture 的同步测试,但存在某些限制。

pytest.importorskip 关于 ImportError 的默认行为

自 8.2 版本弃用。

传统上,pytest.importorskip() 会捕获 ImportError,最初的目的是跳过那些依赖模块未安装的测试,例如使用不同依赖项进行测试。

然而,有些包可能已安装在系统中,但由于其他问题(例如编译错误或损坏的安装)而无法导入。在这种情况下,pytest.importorskip() 仍然会静默跳过测试,但通常用户更希望看到意外错误,以便解决底层问题。

8.2 中,添加了 exc_type 参数,允许用户传递 ModuleNotFoundError,仅当模块确实找不到时才跳过测试,而不是因为其他错误。

默认情况下只捕获 ModuleNotFoundError(并让其他错误传播)将是最佳解决方案,但为了向后兼容,pytest 将保留现有行为,但如果出现以下情况,将引发警告:

  1. 捕获的异常类型为 ImportError,并且

  2. 用户未显式传递 exc_type

如果导入尝试引发 ModuleNotFoundError(常见情况),则跳过模块且不发出警告。

这样,常见情况将保持相同的运行方式,而意外错误现在将发出警告,用户可以通过显式传递 exc_type=ImportError 来抑制警告。

9.0 中,警告将变为错误,在 9.1 中,pytest.importorskip() 将默认仅捕获 ModuleNotFoundError,并且不再发出警告——但用户仍然可以通过将其传递给 exc_type 来捕获 ImportError

Node 构造函数中的 fspath 参数被 pathlib.Path 替换

自 7.0 版本弃用。

为了支持从 py.path.localpathlib 的过渡,Node 构造函数(如 pytest.Function.from_parent()pytest.Class.from_parent())的 fspath 参数现已弃用。

构建节点的插件应传递 pathlib.Path 类型的 path 参数,而不是 fspath 参数。

鼓励实现自定义项和收集器的插件将 fspath 参数(py.path.local)替换为 path 参数(pathlib.Path),并尽可能放弃 py 库的其他用法。

如果可能,带有自定义项的插件应使用 协作构造函数 以避免硬编码它们仅传递给超类的参数。

注意

Node 参数和属性的名称(新属性为 path)与钩子的情况 相反如下所述(旧参数为 path)。

这是由于历史原因造成的不幸遗留问题,随着我们逐渐摆脱 py 依赖(请参阅 #9283 了解更长的讨论),该问题应在未来的版本中解决。

由于像 reportinfo() 这样的方法仍在迁移中,仍然期望返回 py.path.local 对象,因此节点仍然具有 fspath (py.path.local) 和 path (pathlib.Path) 属性,无论构造函数中使用了哪个参数。我们预计在未来的版本中弃用 fspath 属性。

使用标记配置钩子规范/实现

在 pluggy,即 pytest 的插件库,成为独立的包并拥有清晰的 API 之前,pytest 仅使用 pytest.mark 来配置钩子。

pytest.hookimpl()pytest.hookspec() 装饰器已可用多年,应取而代之。

@pytest.mark.tryfirst
def pytest_runtest_call(): ...


# or
def pytest_runtest_call(): ...


pytest_runtest_call.tryfirst = True

应更改为

@pytest.hookimpl(tryfirst=True)
def pytest_runtest_call(): ...

更改了 hookimpl 属性

  • tryfirst

  • trylast

  • optionalhook

  • hookwrapper

更改了 hookwrapper 属性

  • firstresult

  • historic

钩子参数 py.path.localpathlib.Path 替换

自 7.0 版本弃用。

为了支持从 py.path.localpathlib 的过渡,以下钩子现在接收额外的参数

伴随的基于 py.path.local 的路径已被弃用:手动调用这些钩子的插件应只传递新的 pathlib.Path 参数,用户应更改其钩子实现以使用新的 pathlib.Path 参数。

注意

Node 参数和属性的名称,如上所述(新属性为 path),与钩子的情况 相反(旧参数为 path)。

这是由于历史原因造成的不幸遗留问题,随着我们逐渐摆脱 py 依赖(请参阅 #9283 了解更长的讨论),该问题应在未来的版本中解决。

直接构造内部类

自 7.0 版本弃用。

直接构造以下类现已弃用

  • _pytest.mark.structures.Mark

  • _pytest.mark.structures.MarkDecorator

  • _pytest.mark.structures.MarkGenerator

  • _pytest.python.Metafunc

  • _pytest.runner.CallInfo

  • _pytest._code.ExceptionInfo

  • _pytest.config.argparsing.Parser

  • _pytest.config.argparsing.OptionGroup

  • _pytest.pytester.HookRecorder

这些构造函数一直被认为是私有的,但现在会发出弃用警告,这可能会在 pytest 8 中成为硬错误。

pytest.Collectorpytest.Item 之间的菱形继承

自 7.0 版本弃用。

定义一个既是 Item 又是 Collector(例如 File)的自定义 pytest 节点类型现在会发出警告。这从未得到合理支持,并会触发难以调试的错误。

一些提供 linting/代码分析的插件一直将其用作 hack。相反,应使用单独的收集器节点来收集项。请参阅 使用非 Python 测试 以获取示例,以及 修复继承的示例 PR

自定义 Node 子类的构造函数应接受 **kwargs

自 7.0 版本弃用。

如果像 pytest.Item 这样的自定义节点子类重写了 __init__ 方法,它们应该接受 **kwargs。因此,

class CustomItem(pytest.Item):
    def __init__(self, name, parent, additional_arg):
        super().__init__(name, parent)
        self.additional_arg = additional_arg

应改为

class CustomItem(pytest.Item):
    def __init__(self, *, additional_arg, **kwargs):
        super().__init__(**kwargs)
        self.additional_arg = additional_arg

以避免硬编码 pytest 可以传递给超类的参数。有关完整示例,请参阅 使用非 Python 测试

对于没有冲突的情况,不会发出弃用警告。对于有冲突的情况(例如 pytest.File 现在接受 path 而不是 fspath,如 上所述),现在会引发弃用警告。

将标记应用于 fixture 函数

自 7.4 版本弃用。

将标记应用于 fixture 函数从未产生任何效果,但这是一个常见的用户错误。

@pytest.mark.usefixtures("clean_database")
@pytest.fixture
def user() -> User: ...

在这种情况下,用户期望 usefixtures 标记在其预期效果下,即在调用 user 时使用 clean_database fixture,而实际上它根本没有任何效果。

现在 pytest 在遇到此问题时会发出警告,并在未来的版本中引发错误。

yield_fixture 函数/装饰器

自 6.2 版本弃用。

pytest.yield_fixturepytest.fixture() 的弃用别名。

它已经弃用很长时间了,所以可以安全地进行搜索/替换。

移除的功能和破坏性更改

如我们的 向后兼容性政策 所述,弃用功能只会在经过适当的弃用期后在主要版本中移除。

一些无法弃用的破坏性更改也已列出。

yield 测试

在 4.0 版本中移除: yield 测试 xfail

在 8.4 版本中移除: yield 测试引发收集错误。

pytest 不再支持 yield 风格的测试,其中测试函数实际上 yield 函数和值,然后将其转换为适当的测试方法。例如

def check(x, y):
    assert x**x == y


def test_squared():
    yield check, 2, 4
    yield check, 3, 9

这将导致生成两个实际的测试函数。

这种形式的测试函数不支持 fixture,用户应该切换到 pytest.mark.parametrize

@pytest.mark.parametrize("x, y", [(2, 4), (3, 9)])
def test_squared(x, y):
    assert x**x == y

支持为 nose 编写的测试

自 7.2 版本弃用。

在 8.0 版本中移除。

对运行为 nose 编写的测试的支持现已弃用。

nose 多年来一直处于维护模式,并且维护该插件并非易事,因为它会蔓延到整个代码库(有关更多详细信息,请参阅 #9886)。

setup/teardown

可能让用户感到惊讶的一点是,普通的 setupteardown 方法不是 pytest 原生的,它们实际上是 nose 支持的一部分。

class Test:
    def setup(self):
        self.resource = make_resource()

    def teardown(self):
        self.resource.close()

    def test_foo(self): ...

    def test_bar(self): ...

原生 pytest 支持使用 setup_methodteardown_method(请参阅 方法和函数级别的 setup/teardown),因此上述内容应更改为

class Test:
    def setup_method(self):
        self.resource = make_resource()

    def teardown_method(self):
        self.resource.close()

    def test_foo(self): ...

    def test_bar(self): ...

这在整个代码库中通过简单的查找/替换很容易完成。

@with_setup

使用 @with_setup 的代码,例如

from nose.tools import with_setup


def setup_some_resource(): ...


def teardown_some_resource(): ...


@with_setup(setup_some_resource, teardown_some_resource)
def test_foo(): ...

也需要移植到受支持的 pytest 样式。一种方法是使用 fixture

import pytest


def setup_some_resource(): ...


def teardown_some_resource(): ...


@pytest.fixture
def some_resource():
    setup_some_resource()
    yield
    teardown_some_resource()


def test_foo(some_resource): ...

compat_co_firstlineno 属性

Nose 检查函数对象上的此属性,以允许覆盖函数推断的行号。Pytest 不再遵守此属性。

msg= 传递给 pytest.skippytest.failpytest.exit

自 7.0 版本弃用。

在 8.0 版本中移除。

将关键字参数 msg 传递给 pytest.skip()pytest.fail()pytest.exit() 现已弃用,应改用 reason。此更改是为了使这些函数与已接受 reason 参数的 @pytest.mark.skip@pytest.mark.xfail 标记保持一致。

def test_fail_example():
    # old
    pytest.fail(msg="foo")
    # new
    pytest.fail(reason="bar")


def test_skip_example():
    # old
    pytest.skip(msg="foo")
    # new
    pytest.skip(reason="bar")


def test_exit_example():
    # old
    pytest.exit(msg="foo")
    # new
    pytest.exit(reason="bar")

pytest.Instance 收集器

在 7.0 版本中移除。

pytest.Instance 收集器类型已移除。

以前,Python 测试方法被收集为 Class -> Instance -> Function。现在 Class 直接收集测试方法。

大多数引用 Instance 的插件都是为了忽略或跳过它,使用诸如 if isinstance(node, Instance): return 的检查。此类插件应在 pytest >= 7 中简单地移除对 Instance 的考虑。然而,为了保持此类用法的正常工作,已在 pytest.Instance_pytest.python.Instance 中实例化了一个虚拟类型,并且导入它会发出弃用警告。这在 pytest 8 中已移除。

使用 pytest.warns(None)

自 7.0 版本弃用。

在 8.0 版本中移除。

pytest.warns(None) 现已弃用,因为它经常被误用。它的正确用法是检查代码是否至少发出了任何类型的警告——就像 pytest.warns()pytest.warns(Warning)

有关示例,请参阅 测试中警告的其他用例

Parser.addoption 中的向后兼容性

自 2.4 版本弃用。

在 8.0 版本中移除。

Parser.addoption 的几种行为在 pytest 8 中现已移除(自 pytest 2.4.0 起弃用)

  • parser.addoption(..., help=".. %default ..") - 改用 %(default)s

  • parser.addoption(..., type="int/string/float/complex") - 改用 type=int 等。

--strict 命令行选项(重新引入)

自 6.2 版本弃用。

在 9.0 版本中更改。

--strict 命令行选项已被弃用,取而代之的是 --strict-markers,它更好地表达了该选项的作用。

在 8.1 版本中,我们意外地取消了对 --strict 的弃用。

在 9.0 版本中,我们更改了 --strict,使其设置新的 strict 配置选项。它现在启用所有与严格性相关的选项(包括 strict_markers)。

实现 pytest_cmdline_preparse 钩子

自 7.0 版本弃用。

在 8.0 版本中移除。

实现 pytest_cmdline_preparse 钩子已正式弃用。请改为实现 pytest_load_initial_conftests 钩子。

def pytest_cmdline_preparse(config: Config, args: List[str]) -> None: ...


# becomes:


def pytest_load_initial_conftests(
    early_config: Config, parser: Parser, args: List[str]
) -> None: ...

pytest 8 中的收集更改

添加了一个新的 pytest.Directory 基础收集节点,所有用于文件系统目录的收集器节点都应继承自该节点。这类似于现有用于文件节点的 pytest.File

pytest.Package 更改为 pytest.Directory 的子类。Package 表示一个文件系统目录,它是一个 Python 包,即包含一个 __init__.py 文件。

pytest.Package 现在只收集其自身目录中的文件;以前它会递归收集。子目录作为子收集器节点进行收集,从而创建一个镜像文件系统层次结构的收集树。

session.name 现在是 "";以前是根目录名。这与一直为 ""session.nodeid 相匹配。

添加了一个新的 pytest.Dir 具体收集节点,它是 pytest.Directory 的子类。此节点表示一个文件系统目录,它不是 pytest.Package,即不包含 __init__.py 文件。与 Package 类似,它只收集其自身目录中的文件,同时将子目录作为子收集器节点进行收集。

文件和目录现在联合按字母顺序收集,除非插件更改。以前,文件在目录之前收集。

现在,收集树包含直到 rootdir 的目录/包,适用于在 rootdir 中找到的初始参数。对于 rootdir 之外的文件,只收集直接目录/包——但请注意,不鼓励从 rootdir 之外进行收集。

例如,给定以下文件系统树

myroot/
    pytest.ini
    top/
    ├── aaa
    │   └── test_aaa.py
    ├── test_a.py
    ├── test_b
    │   ├── __init__.py
    │   └── test_b.py
    ├── test_c.py
    └── zzz
        ├── __init__.py
        └── test_zzz.py

收集树,如 pytest --collect-only top/ 所示,但为了清晰起见添加了否则隐藏的 Session 节点,现在如下所示

<Session>
  <Dir myroot>
    <Dir top>
      <Dir aaa>
        <Module test_aaa.py>
          <Function test_it>
      <Module test_a.py>
        <Function test_it>
      <Package test_b>
        <Module test_b.py>
          <Function test_it>
      <Module test_c.py>
        <Function test_it>
      <Package zzz>
        <Module test_zzz.py>
          <Function test_it>

以前是这样

<Session>
  <Module top/test_a.py>
    <Function test_it>
  <Module top/test_c.py>
    <Function test_it>
  <Module top/aaa/test_aaa.py>
    <Function test_it>
  <Package test_b>
    <Module test_b.py>
      <Function test_it>
  <Package zzz>
    <Module test_zzz.py>
      <Function test_it>

依赖于收集树特定形状的代码/插件可能需要更新。

pytest.Package 不再是 pytest.Modulepytest.File

在 8.0 版本中更改。

Package 收集器节点指定一个 Python 包,即一个包含 __init__.py 文件的目录。以前 Packagepytest.Module 的子类型(表示一个 Python 模块),该模块是 __init__.py 文件。这已被认为是一个设计错误(详见 #11137#7777)。

Package 节点的 path 属性现在指向包目录而不是 __init__.py 文件。

请注意,__init__.pyModule 节点(它不是 Package)仍然可能存在,如果在收集期间被拾取(例如,如果您将 python_files 配置为包含 __init__.py 文件)。

收集 __init__.py 文件不再收集包

在 8.0 版本中移除。

运行 pytest pkg/__init__.py 现在只收集 pkg/__init__.py 文件(模块)。以前,它会收集整个 pkg 包,包括目录中的其他测试文件,但不包括 __init__.py 文件本身的测试(除非 python_files 被更改为允许 __init__.py 文件)。

要收集整个包,只需指定目录:pytest pkg

pytest.collect 模块

自 6.0 版本弃用。

在 7.0 版本中移除。

pytest.collect 模块不再是公共 API 的一部分,其所有名称现在都应直接从 pytest 导入。

pytest_warning_captured 钩子

自 6.0 版本弃用。

在 7.0 版本中移除。

此钩子有一个 item 参数,pytest-xdist 无法对其进行序列化。

请改用 pytest_warning_recorded 钩子,它将 item 参数替换为 nodeid 参数。

pytest._fillfuncargs 函数

自 6.0 版本弃用。

在 7.0 版本中移除。

此函数是为了向后兼容旧插件而保留的。

它的功能不应直接使用,但如果您必须替换它,请改用 function._request._fillfixtures(),但请注意,这不是公共 API,将来可能会中断。

--no-print-logs 命令行选项

自 5.4 版本弃用。

在 6.0 版本中移除。

--no-print-logs 选项和 log_print ini 设置已移除。如果您使用过它们,请改用 --show-capture

pytest 3.5.0 中添加了 --show-capture 命令行选项,允许指定在测试失败时如何显示捕获的输出:nostdoutstderrlogall(默认)。

结果日志 (--result-log)

自 4.0 版本弃用。

在 6.0 版本中移除。

--result-log 选项生成一个测试报告流,可以在运行时进行分析,但它使用自定义格式,需要用户实现自己的解析器。

pytest-reportlog 插件提供了一个 --report-log 选项,它是一个更标准和可扩展的替代方案,每行生成一个 JSON 对象,并且应该涵盖相同的用例。请试用并提供反馈。

pytest-reportlog 插件甚至可能在某个时候合并到核心中,这取决于插件的计划和使用它的用户数量。

pytest_collect_directory 钩子

在 6.0 版本中移除。

pytest_collect_directory 钩子多年来一直未能正常工作(它被调用但结果被忽略)。用户可以考虑改用 pytest_collection_modifyitems

TerminalReporter.writer

在 6.0 版本中移除。

TerminalReporter.writer 属性已弃用,不应再使用。这被无意中作为该插件公共 API 的一部分暴露,并将其与 py.io.TerminalWriter 紧密联系在一起。

直接使用 TerminalReporter.writer 的插件应改用提供相同功能的 TerminalReporter 方法。

junit_family 默认值更改为 “xunit2”

在 6.0 版本中更改。

junit_family 选项的默认值在 pytest 6.0 中将更改为 xunit2,这是旧 xunit1 格式的更新,并且现代工具默认支持此文件类型(例如 Jenkins、Azure Pipelines 等)。

建议用户尝试新的 xunit2 格式,看看他们使用 JUnit XML 文件的工具是否支持它。

要使用新格式,请更新您的配置文件

[pytest]
junit_family = "xunit2"
[pytest]
junit_family = xunit2

如果您发现您的工具不支持新格式,并且希望继续使用旧版本,请将选项设置为 legacy

[pytest]
junit_family = "legacy"
[pytest]
junit_family = legacy

通过使用 legacy,您将在升级到 pytest 6.0 时继续使用旧版/xunit1 格式,届时默认格式将是 xunit2

为了让用户了解此过渡,如果命令行中提供了 --junit-xml 选项,但 junit_family 未在 pytest.ini 中显式配置,pytest 将发出警告。

已知支持 xunit2 格式的服务

节点构造更改为 Node.from_parent

在 6.0 版本中更改。

现在,节点的构造应使用命名构造函数 from_parent。API 表面的这种限制旨在实现对收集树的更好/更简单的重构。

这意味着,现在必须调用 MyItem.from_parent(collector, name="foo"),而不是 MyItem(name="foo", parent=collector, obj=42)

希望支持旧版本 pytest 并抑制警告的插件可以使用 hasattr 检查该版本中是否存在 from_parent

def pytest_pycollect_makeitem(collector, name, obj):
    if hasattr(MyItem, "from_parent"):
        item = MyItem.from_parent(collector, name="foo")
        item.obj = 42
        return item
    else:
        return MyItem(name="foo", parent=collector, obj=42)

请注意,from_parent 应该只使用关键字参数调用。

pytest.fixture 参数仅限关键字

在 6.0 版本中移除。

将参数作为位置参数传递给 pytest.fixture() 已移除 - 请改为使用关键字传递它们。

funcargnamesfixturenames 的别名

在 6.0 版本中移除。

FixtureRequestMetafuncFunction 类通过 aptly-named fixturenames 属性跟踪其关联 fixture 的名称。

在 pytest 2.3 之前,此属性名为 funcargnames,此后我们一直将其保留为别名。现在它终于要被移除,因为它在我们需要区分 fixture 名称和非 fixture(例如 pytest.mark.parametrize)提供的名称的地方经常引起混淆。

pytest.config 全局对象

在 5.0 版本中移除。

pytest.config 全局对象已弃用。请改用 request.config(通过 request fixture),或者如果您是插件作者,请使用 pytest_configure(config) 钩子。请注意,许多钩子也可以间接访问 config 对象,例如通过 session.configitem.config

pytest.raises"message" 参数

在 5.0 版本中移除。

人们常误认为此参数会匹配异常消息,而实际上它仅在 pytest.raises 检查失败时提供自定义消息。为防止用户犯此错误,并且因为它被认为使用很少,pytest 正在弃用它,目前不提供替代方案。

如果您对此参数有合理的用例,请考虑为了获得相同的结果,您可以在 with 语句结束时手动调用 pytest.fail

例如

with pytest.raises(TimeoutError, message="Client got unexpected message"):
    wait_for(websocket.recv(), 0.5)

变为

with pytest.raises(TimeoutError):
    wait_for(websocket.recv(), 0.5)
    pytest.fail("Client got unexpected message")

如果您仍然对此弃用和未来的移除有疑问,请在 #3974 上发表评论。

使用字符串作为第二个参数的 raises / warns

在 5.0 版本中移除。

请改用这些的上下文管理器形式。必要时,直接调用 exec

示例

pytest.raises(ZeroDivisionError, "1 / 0")
pytest.raises(SyntaxError, "a $ b")

pytest.warns(DeprecationWarning, "my_function()")
pytest.warns(SyntaxWarning, "assert(1, 2)")

变为

with pytest.raises(ZeroDivisionError):
    1 / 0
with pytest.raises(SyntaxError):
    exec("a $ b")  # exec is required for invalid syntax

with pytest.warns(DeprecationWarning):
    my_function()
with pytest.warns(SyntaxWarning):
    exec("assert(1, 2)")  # exec is used to avoid a top-level warning

在自定义收集器中使用 Class

在 4.0 版本中移除。

Collector 子类中使用名为 "Class" 的对象来自定义收集的节点类型已弃用。用户应改用 pytest_pycollect_makeitem 在收集期间自定义节点类型。

此问题应仅影响创建新收集类型的高级插件,因此如果您看到此警告消息,请联系作者,以便他们更改代码。

pytest.mark.parametrize 中的标记

在 4.0 版本中移除。

将标记应用于 pytest.mark.parametrize 调用的值现已弃用。例如

@pytest.mark.parametrize(
    "a, b",
    [
        (3, 9),
        pytest.mark.xfail(reason="flaky")(6, 36),
        (10, 100),
        (20, 200),
        (40, 400),
        (50, 500),
    ],
)
def test_foo(a, b): ...

此代码将 pytest.mark.xfail(reason="flaky") 标记应用于上述参数化调用的 (6, 36) 值。

这被认为难以阅读和理解,并且其实现也给代码带来了问题,阻碍了标记架构的进一步内部改进。

要更新代码,请使用 pytest.param

@pytest.mark.parametrize(
    "a, b",
    [
        (3, 9),
        pytest.param(6, 36, marks=pytest.mark.xfail(reason="flaky")),
        (10, 100),
        (20, 200),
        (40, 400),
        (50, 500),
    ],
)
def test_foo(a, b): ...

pytest_funcarg__ 前缀

在 4.0 版本中移除。

在很早期的 pytest 版本中,fixture 可以使用 pytest_funcarg__ 前缀定义

def pytest_funcarg__data():
    return SomeData()

切换到 @pytest.fixture 装饰器

@pytest.fixture
def data():
    return SomeData()

setup.cfg 文件中的 [pytest] 部分

在 4.0 版本中移除。

setup.cfg 文件中的 [pytest] 部分现在应命名为 [tool:pytest],以避免与其他 distutils 命令冲突。

Metafunc.addcall

在 4.0 版本中移除。

Metafunc.addcall 是当前参数化机制的前身。用户应改用 pytest.Metafunc.parametrize()

示例

def pytest_generate_tests(metafunc):
    metafunc.addcall({"i": 1}, id="1")
    metafunc.addcall({"i": 2}, id="2")

变为

def pytest_generate_tests(metafunc):
    metafunc.parametrize("i", [1, 2], ids=["1", "2"])

cached_setup

在 4.0 版本中移除。

request.cached_setup 是 fixture 可用的 setup/teardown 机制的前身。

示例

@pytest.fixture
def db_session():
    return request.cached_setup(
        setup=Session.create, teardown=lambda session: session.close(), scope="module"
    )

这应该更新以利用标准 fixture 机制

@pytest.fixture(scope="module")
def db_session():
    session = Session.create()
    yield session
    session.close()

您可以查阅 文档中的 funcarg 比较部分 以获取更多信息。

非顶级 conftest 文件中的 pytest_plugins

在 4.0 版本中移除。

在非顶级 conftest.py 文件中定义 pytest_plugins 现已弃用,因为它们将 全局 激活引用的插件,这令人惊讶,因为对于所有其他 pytest 功能,conftest.py 文件仅对其自身或其下方的测试 有效

Config.warnNode.warn

在 4.0 版本中移除。

这些方法是内部 pytest 警告系统的一部分,但自 3.8 版本以来,pytest 将内置警告系统用于其自身的警告,因此这两个函数现已弃用。

Config.warn 应该被标准 warnings.warn 的调用替换,例如

config.warn("C1", "some warning")

变为

warnings.warn(pytest.PytestWarning("some warning"))

Node.warn 现在支持两种签名

  • node.warn(PytestWarning("some message")): 现在是调用此函数的 推荐 方式。警告实例必须是 PytestWarning 或其子类。

  • node.warn("CI", "some message"): 此代码/消息形式已 移除,应转换为上述警告实例形式。

record_xml_property

在 4.0 版本中移除。

record_xml_property fixture 现已弃用,取而代之的是更通用的 record_property,后者可供其他消费者(例如 pytest-html)用于获取有关测试运行的自定义信息。

这仅仅是重命名 fixture 的问题,因为 API 相同

def test_foo(record_xml_property): ...

更改为

def test_foo(record_property): ...

将命令行字符串传递给 pytest.main()

在 4.0 版本中移除。

将命令行字符串传递给 pytest.main() 已弃用

pytest.main("-v -s")

改为传递一个列表

pytest.main(["-v", "-s"])

通过传递字符串,用户期望 pytest 将使用他们正在工作的 shell 规则(例如 bashPowershell)解释该命令行,但这很难/不可能以可移植的方式实现。

直接调用 fixture

在 4.0 版本中移除。

直接调用 fixture 函数,而不是在测试函数中请求它们,已弃用。

例如

@pytest.fixture
def cell():
    return ...


@pytest.fixture
def full_cell():
    cell = cell()
    cell.make_full()
    return cell

这给新用户带来了很大的困惑,他们经常互换地调用 fixture 函数和从测试函数中请求它们,这破坏了 fixture 解析模型。

在这些情况下,只需在依赖的 fixture 中直接请求函数

@pytest.fixture
def cell():
    return ...


@pytest.fixture
def full_cell(cell):
    cell.make_full()
    return cell

或者,如果 fixture 函数在测试中被多次调用(使其难以应用上述模式)或者如果您想对代码进行最小的更改,您可以创建一个 fixture,它与 name 参数一起调用原始函数

def cell():
    return ...


@pytest.fixture(name="cell")
def cell_fixture():
    return cell()

通过 Node 访问内部类

在 4.0 版本中移除。

通过 Node 实例访问 ModuleFunctionClassInstanceFileItem 现在会发出此警告

usage of Function.Module is deprecated, please use pytest.Module instead

用户应该只需 import pytest 并使用 pytest 模块访问这些对象。

这已记录为弃用多年,但直到现在我们才实际发出弃用警告。

Node.get_marker

在 4.0 版本中移除。

作为大型 标记改进和迭代 的一部分,_pytest.nodes.Node.get_marker 已移除。请参阅 文档 中关于如何更新代码的提示。

somefunction.markname

在 4.0 版本中移除。

作为大型 标记改进和迭代 的一部分,我们已经弃用了 MarkInfo 的使用,获取元素标记的唯一正确方法是通过 node.iter_markers(name)

pytest_namespace

在 4.0 版本中移除。

此钩子已弃用,因为它极大地复杂化了 pytest 在配置和初始化方面的内部机制,使得一些错误修复和重构变得不可能。

使用示例

class MySymbol: ...


def pytest_namespace():
    return {"my_symbol": MySymbol()}

依赖此钩子的插件作者应改为要求用户现在直接导入插件模块(带有适当的公共 API)。

作为权宜之计,插件作者仍然可以在 pytest 的命名空间中注入他们的名称,通常是在 pytest_configure 期间

import pytest


def pytest_configure():
    pytest.my_symbol = MySymbol()