pytest fixture-请求fixture
什么是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
对该对象的影响。