pytest fixture-请求fixture

2022-03-23 14:33 更新

什么是fixture

在测试中,​fixture​为测试 提供了一个定义好的、可靠的和一致的上下文。这可能包括环境(例如配置有已知参数的数据库)或内容(例如数据集)。
Fixtures ​定义了构成测试排列阶段的步骤和数据。在 pytest 中,它们是您定义的用于此目的的函数。它们也可以用来定义测试的行为阶段;这是设计更复杂测试的强大技术。
由​fixture​设置的服务、状态或其他操作环境由测试函数通过参数访问。对于测试函数使用的每个​fixture​,在测试函数的定义中通常都有一个参数(以​fixture​命名)

在基本级别上,测试函数通过将​​fixture​​声明为参数来请求它们所需要的​​fixture​​。

当pytest运行一个测试时,它会查看该测试函数签名中的参数,然后搜索与这些参数具有相同名称的​​fixture​​。一旦pytest找到它们,它就运行这些​​fixture​​,捕获它们返回的内容(如果有的话),并将这些对象作为参数传递给测试函数。

快速示例

import pytest


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

    def cube(self):
        self.cubed = True


class FruitSalad:
    def __init__(self, *fruit_bowl):
        self.fruit = fruit_bowl
        self._cube_fruit()

    def _cube_fruit(self):
        for fruit in self.fruit:
            fruit.cube()


# Arrange
@pytest.fixture
def fruit_bowl():
    return [Fruit("apple"), Fruit("banana")]


def test_fruit_salad(fruit_bowl):
    # Act
    fruit_salad = FruitSalad(*fruit_bowl)

    # Assert
    assert all(fruit.cubed for fruit in fruit_salad.fruit)

在这个例子中,​​test_fruit_salad​​请求​​fruit_bowl​​(即​​def test_fruit_salad(fruit_bowl):​​),当pytest看到这个时,它将执行​​fruit_bowl fixture​​函数,并将它返回的对象作为​​fruit_bowl​​参数传递给​​test_fruit_salad​​

如果我们手动进行,大致会发生以下情况:

def fruit_bowl():
    return [Fruit("apple"), Fruit("banana")]


def test_fruit_salad(fruit_bowl):
    # Act
    fruit_salad = FruitSalad(*fruit_bowl)

    # Assert
    assert all(fruit.cubed for fruit in fruit_salad.fruit)


# Arrange
bowl = fruit_bowl()
test_fruit_salad(fruit_bowl=bowl)

Fixtures可以请求其他fixtures

pytest最大的优势之一是它极其灵活的​​fixture​​系统。它允许我们将测试的复杂需求归结为更简单和更有组织的功能,我们只需要让每个功能描述它们所依赖的东西。我们将进一步深入讨论这个问题,但现在,这里有一个快速的例子来演示​​fixtures​​如何使用其他​​fixtures​​:

# contents of test_append.py
import pytest


# Arrange
@pytest.fixture
def first_entry():
    return "a"


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


def test_string(order):
    # Act
    order.append("b")

    # Assert
    assert order == ["a", "b"]

请注意,这与上面的示例相同,但变化很小。 pytest 中的​​fixture​请求​​fixture ​就像测试一样。 所有相同的请求规则都适用于用于测试的​​fixture​​。 如果我们手动完成,这个例子的工作方式如下:

def first_entry():
    return "a"


def order(first_entry):
    return [first_entry]


def test_string(order):
    # Act
    order.append("b")

    # Assert
    assert order == ["a", "b"]


entry = first_entry()
the_list = order(first_entry=entry)
test_string(order=the_list)

Fixtures可重复使用

使pytest的​​fixture​​系统如此强大的原因之一是,它使我们能够定义一个通用的设置步骤,这个步骤可以重复使用,就像使用一个普通函数一样。两个不同的测试可以请求相同的​​fixture​​,并让pytest从该​​fixture​​为每个测试提供自己的结果。

这对于确保测试不会相互影响非常有用。 我们可以使用这个系统来确保每个测试都获得自己的新一批数据,并从干净的状态开始,这样它就可以提供一致的、可重复的结果。

下面是一个例子,说明这是如何派上用场的:

# contents of test_append.py
import pytest


# Arrange
@pytest.fixture
def first_entry():
    return "a"


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


def test_string(order):
    # Act
    order.append("b")

    # Assert
    assert order == ["a", "b"]


def test_int(order):
    # Act
    order.append(2)

    # Assert
    assert order == ["a", 2]

这里的每个测试都有它自己的列表对象的副本,这意味着​​order fixture​​被执行两次(​​first_entry fixture​​也是如此)。如果我们手动执行,它看起来会是这样的:

def first_entry():
    return "a"


def order(first_entry):
    return [first_entry]


def test_string(order):
    # Act
    order.append("b")

    # Assert
    assert order == ["a", "b"]


def test_int(order):
    # Act
    order.append(2)

    # Assert
    assert order == ["a", 2]


entry = first_entry()
the_list = order(first_entry=entry)
test_string(order=the_list)

entry = first_entry()
the_list = order(first_entry=entry)
test_int(order=the_list)

一个test/fixture一次可以请求多个fixture

测试和​​fixture​​不限于一次请求单个​​fixture​​。他们想要多少就可以要多少。下面是另一个快速演示的例子:

# contents of test_append.py
import pytest


# Arrange
@pytest.fixture
def first_entry():
    return "a"


# Arrange
@pytest.fixture
def second_entry():
    return 2


# Arrange
@pytest.fixture
def order(first_entry, second_entry):
    return [first_entry, second_entry]


# Arrange
@pytest.fixture
def expected_list():
    return ["a", 2, 3.0]


def test_string(order, expected_list):
    # Act
    order.append(3.0)

    # Assert
    assert order == expected_list

每个测试可以请求fixture多次(缓存返回值)

在同一测试期间,​​fixture​​也可以被请求多次,pytest不会为该测试再次执行它们。这意味着我们可以请求多个依赖于它们的​​fixture​​(甚至在测试本身中的​​fixture​​),而不需要执行多次这些​​fixture​​。

# contents of test_append.py
import pytest


# Arrange
@pytest.fixture
def first_entry():
    return "a"


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


# Act
@pytest.fixture
def append_first(order, first_entry):
    return order.append(first_entry)


def test_string_only(append_first, order, first_entry):
    # Assert
    assert order == [first_entry]

如果一个被请求的​​fixture​​在测试期间每次被请求时都被执行一次,那么这个测试将会失败,因为​​append_first​​和​​test_string_only​​都会将​​order​​视为一个空列表,但由于​​order​​的返回值在第一次被调用后被缓存(以及执行它可能有的任何副作用),​​test​​和​​append_first​​都引用了同一个对象,测试中看到了​​append_first​​对该对象的影响。


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号