关于 fixture

另请参见

如何使用 fixtures

另请参见

Fixtures 参考

pytest fixture 设计得既明确又具有模块化和可扩展性。

什么是 fixture

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

Fixture 定义了构成测试 arrange(安排)阶段的步骤和数据(参见 测试的剖析)。在 pytest 中,它们是你为实现此目的而定义的函数。它们也可以用来定义测试的 act(操作)阶段;这是一种设计更复杂测试的强大技术。

测试函数通过参数来访问由 fixture 设置的服务、状态或其他运行环境。对于测试函数所使用的每个 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。它们可以依赖于任意数量的 fixture,而且 fixture 也可以使用其他 fixture。这正是 pytest 的 fixture 系统真正闪光的地方。

对比 xUnit 风格 setup/teardown 函数的改进

pytest fixture 对经典的 xUnit 风格 setup/teardown 函数进行了重大改进:

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

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

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

  • 无论使用多少个 fixture,清理(teardown)逻辑都能被轻松且安全地管理,无需手动谨慎处理错误或微观管理清理步骤添加的顺序。

此外,pytest 继续支持 如何实现 xunit 风格的设置。你可以混合使用这两种风格,根据自己的偏好逐步从经典风格过渡到新风格。你也可以从现有的 unittest.TestCase 风格 开始。

Fixture 错误

pytest 会尽力将给定测试的所有 fixture 按线性顺序排列,以便确定哪个 fixture 先执行、哪个后执行。然而,如果较早的 fixture 出现问题并引发异常,pytest 将停止为该测试执行剩余的 fixture,并将该测试标记为“错误”(error)。

当一个测试被标记为“错误”时,并不意味着测试失败(failed)。这仅仅意味着测试甚至无法执行,因为它所依赖的事物之一出现了问题。

这就是为什么对于给定的测试,尽可能减少不必要的依赖是一个好主意。这样,无关事物的问题就不会导致我们无法全面了解哪些部分可能存在问题。

这里有一个简短的示例来解释这一点:

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 的任何其他 fixture,甚至不会尝试运行 test_order 本身。唯一运行过的将是 orderappend_first

共享测试数据

如果你想让文件中的测试数据可供测试使用,一种好的方法是在 fixture 中加载这些数据以供测试使用。这利用了 pytest 的自动缓存机制。

另一种好方法是将数据文件添加到 tests 目录中。此外还有社区插件可以帮助管理这方面的测试,例如 pytest-datadirpytest-datafiles

关于 fixture 清理的注意事项

pytest 不会对 SIGTERMSIGQUIT 信号进行特殊处理(SIGINT 由 Python 运行时通过 KeyboardInterrupt 自然处理),因此,对于那些在 Python 进程被这些信号终止时必须进行清理的外部资源,其对应的 fixture 可能会导致资源泄漏。

pytest 不处理这些信号来执行 fixture 清理的原因是:信号处理器(signal handlers)是全局的,修改它们可能会干扰正在执行的代码。

如果你的测试套件中的 fixture 在这些场景下需要特别注意终止问题,请参阅问题追踪器中的 这条评论,以获取可能的变通方法。