废弃和移除

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

废弃功能

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

依赖于异步 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 参数现已废弃。

构造节点的插件应该传递 path 参数(类型为 pathlib.Path),而不是 fspath 参数。

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

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

注意

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

这由于历史原因是一个不幸的产物,随着我们慢慢摆脱 py 依赖,这个问题应该在未来版本中得到解决(有关更长的讨论,请参阅 #9283)。

由于 reportinfo() 等方法仍在进行迁移,该方法仍应返回 py.path.local 对象,因此节点仍具有 fspathpy.path.local)和 pathpathlib.Path)属性,无论构造函数中使用了哪个参数。我们预计在未来版本中废弃 fspath 属性。

使用标记配置 hook spec/impls

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

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

hooks 的 py.path.local 参数被 pathlib.Path 替换

自版本 7.0 废弃。

为了支持从 py.path.localpathlib 的转换,以下 hooks 现在接收额外的参数:

相应的基于 py.path.local 的路径已废弃:手动调用这些 hooks 的插件应仅传递新的 pathlib.Path 参数,用户应更改其 hook 实现以使用新的 pathlib.Path 参数。

注意

Node 参数和属性的名称,如上所述(新属性为 path),与 hooks 的情况(旧参数为 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 的自定义 pytest 节点类型(例如 File)现在会发出警告。这从未被合理支持,并且会触发难以调试的错误。

一些提供 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: ...

用户期望在这种情况下,当调用 user 时,usefixtures 标记会产生使用 clean_database fixture 的预期效果,而实际上它根本没有效果。

现在,当 pytest 遇到这个问题时,它将发出警告,并在未来的版本中引发错误。

在测试函数中返回非 None 值

自版本 7.2 废弃。

如果测试函数返回除 None 以外的值,现在会发出 pytest.PytestReturnNotNoneWarning

这避免了初学者常犯的一个错误,他们期望返回一个 bool 会导致测试通过或失败,例如:

@pytest.mark.parametrize(
    ["a", "b", "result"],
    [
        [1, 2, 5],
        [2, 3, 8],
        [5, 3, 18],
    ],
)
def test_foo(a, b, result):
    return foo(a, b) == result

鉴于 pytest 会忽略返回值,这可能令人惊讶,因为它永远不会失败。

正确的做法是将 return 改为 assert

@pytest.mark.parametrize(
    ["a", "b", "result"],
    [
        [1, 2, 5],
        [2, 3, 8],
        [5, 3, 18],
    ],
)
def test_foo(a, b, result):
    assert foo(a, b) == result

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 不再尊重此属性。

pytest.skippytest.failpytest.exit 传递 msg=

自版本 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 废弃。

在版本 8.0 中移除。

--strict 命令行选项已废弃,取而代之的是 --strict-markers,它更好地传达了该选项的功能。

我们计划未来可能会重新引入 --strict,并使其成为所有严格性相关选项的包容性标志(目前是 --strict-markers--strict-config,未来可能会引入更多)。

实现 pytest_cmdline_preparse hook

自版本 7.0 废弃。

在版本 8.0 中移除。

实现 pytest_cmdline_preparse hook 已正式废弃。请改为实现 pytest_load_initial_conftests hook。

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 hook

自版本 6.0 废弃。

在版本 7.0 中移除。

此 hook 包含一个 item 参数,该参数无法通过 pytest-xdist 序列化。

请改用 pytest_warning_recorded hook,它将 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 hook

在版本 6.0 中移除。

pytest_collect_directory hook 多年来一直无法正常工作(它被调用但结果被忽略)。用户可以考虑改用 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.ini

[pytest]
junit_family=xunit2

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

[pytest]
junit_family=legacy

通过使用 legacy,当升级到 pytest 6.0 时,您将继续使用 legacy/xunit1 格式,其中默认格式将是 xunit2

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

已知支持 xunit2 格式的服务

节点构造更改为 Node.from_parent

在版本 6.0 中更改。

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

这意味着以前使用 MyItem(name="foo", parent=collector, obj=42) 的方式,现在必须调用 MyItem.from_parent(collector, name="foo")

希望支持旧版本 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 类通过其恰当命名的 fixturenames 属性跟踪其关联 fixture 的名称。

在 pytest 2.3 之前,此属性名为 funcargnames,自那时起我们一直将其保留为别名。现在它终于要被移除,因为它在我们需要或插件作者必须区分 fixture 名称和非 fixture(例如 pytest.mark.parametrize)提供的名称时常常引起混淆。

pytest.config 全局对象

在版本 5.0 中移除。

pytest.config 全局对象已废弃。请改用 request.config(通过 request fixture)或如果你是插件作者,请使用 pytest_configure(config) hook。请注意,许多 hook 也可以通过 session.configitem.config 等间接访问 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 中移除。

此 hook 已废弃,因为它极大地复杂了 pytest 内部关于配置和初始化的机制,使得一些错误修复和重构变得不可能。

用法示例:

class MySymbol: ...


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

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

作为权宜之计,插件作者仍可在 pytest 的命名空间中注入其名称,通常在 pytest_configure 期间:

import pytest


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