关于 fixtures

About fixtures

pytest fixtures 装置设计得明确、模块化且可扩展。

什么是 Fixture

What fixtures are

在测试中, fixture 提供了一个定义良好、可靠且一致的测试上下文。这可以包括环境(例如配置了已知参数的数据库)或内容(例如数据集)。

Fixtures 定义了构成测试 arrange 阶段的步骤和数据(参见 测试剖析)。在 pytest 中,它们是您定义的函数,用于实现此目的。它们还可以用于定义测试的 act 阶段;这是一种设计更复杂测试的强大技术。

由 fixtures 设置的服务、状态或其他操作环境通过参数被测试函数访问。对于每个测试函数使用的 fixture,通常在测试函数的定义中会有一个参数(以 fixture 的名称命名)。

我们可以通过使用装饰器 @pytest.fixture 来告诉 pytest 某个特定函数是一个 fixture。以下是 pytest 中 fixture 的简单示例:

import pytest


class Fruit:
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        return self.name == other.name


@pytest.fixture
def my_fruit():
    return Fruit("apple")


@pytest.fixture
def fruit_basket(my_fruit):
    return [Fruit("banana"), my_fruit]


def test_my_fruit_in_basket(my_fruit, fruit_basket):
    assert my_fruit in fruit_basket

测试不必仅限于单个 fixture。它们可以依赖于您想要的任意多个 fixtures,且 fixtures 也可以使用其他 fixtures。这正是 pytest 的 fixture 系统真正闪耀的地方。

对 xUnit 样式的安装/拆卸函数的改进

Improvements over xUnit-style setup/teardown functions

pytest fixtures 相较于经典的 xUnit 风格的设置/拆卸函数提供了显著的改进:

  • fixtures 具有明确的名称,并通过在测试函数、模块、类或整个项目中声明其使用来激活。

  • fixtures 以模块化方式实现,因为每个 fixture 名称触发一个 fixture function,该函数本身可以使用其他 fixtures。

  • fixture 管理可以从简单的单元测试扩展到复杂的功能测试,允许根据配置和组件选项参数化 fixtures 和测试,或在函数、类、模块或整个测试会话范围内重用 fixtures。

  • 拆卸逻辑可以轻松、安全地管理,无论使用多少 fixtures,无需手动小心处理错误或微观管理清理步骤的添加顺序。

此外,pytest 继续支持 如何实现 xunit 样式的设置。您可以根据自己的喜好混合这两种风格,逐步从经典风格迁移到新风格。您还可以从现有的 unittest.TestCase style 开始。

Fixture 错误

Fixture errors

pytest 尽力将给定测试的所有 fixtures 按线性顺序排列,以便它可以看到哪个 fixture 是第一个、第二个、第三个,依此类推。然而,如果早期的 fixture 发生问题并引发异常,pytest 将停止执行该测试的 fixtures,并将测试标记为有错误。

当测试被标记为有错误时,并不意味着测试失败。这只是意味着由于它依赖的某个东西出现了问题,测试无法被执行。

这也是为什么尽可能减少给定测试的不必要依赖是个好主意。这样,某个无关的事物出现问题不会导致我们对可能存在的问题的完整性产生误解。

以下是一个快速示例来帮助解释:

import pytest


@pytest.fixture
def order():
    return []


@pytest.fixture
def append_first(order):
    order.append(1)


@pytest.fixture
def append_second(order, append_first):
    order.extend([2])


@pytest.fixture(autouse=True)
def append_third(order, append_second):
    order += [3]


def test_order(order):
    assert order == [1, 2, 3]

如果由于某种原因,order.append(1) 有一个 bug 并引发异常,我们将无法知道 order.extend([2])order += [3] 是否也会有问题。在 append_first 抛出异常后,pytest 将不再运行 test_order 的任何其他 fixtures,甚至不会尝试运行 test_order 本身。唯一会运行的只是 orderappend_first

共享测试数据

Sharing test data

如果您想将来自文件的测试数据提供给您的测试,一个好的方法是通过在 fixture 中加载这些数据,以供您的测试使用。这利用了 pytest 的自动缓存机制。

另一种好的方法是在 tests 文件夹中添加数据文件。还有一些社区插件可帮助管理测试的这一方面,例如 pytest-datadirpytest-datafiles

关于 Fixture 清理的说明

A note about fixture cleanup

pytest 对 SIGTERMSIGQUIT 信号没有进行任何特殊处理(SIGINT 由 Python 运行时通过 KeyboardInterrupt 自然处理),因此管理外部资源的 fixtures 在 Python 进程被这些信号终止时,可能会导致资源泄漏。

pytest 不处理这些信号以执行 fixture 清理的原因是,信号处理程序是全局的,改变它们可能会干扰正在执行的代码。

如果您测试套件中的 fixtures 在这些情况下需要特别处理终止,请参阅问题跟踪器中的 this comment 以获取可能的解决方法。