在測試中,?fixture
?為測試 提供了一個定義好的、可靠的和一致的上下文。這可能包括環(huán)境(例如配置有已知參數(shù)的數(shù)據(jù)庫)或內(nèi)容(例如數(shù)據(jù)集)。
?Fixtures
?定義了構(gòu)成測試排列階段的步驟和數(shù)據(jù)。在 pytest 中,它們是您定義的用于此目的的函數(shù)。它們也可以用來定義測試的行為階段;這是設計更復雜測試的強大技術(shù)。
由?fixture
?設置的服務、狀態(tài)或其他操作環(huán)境由測試函數(shù)通過參數(shù)訪問。對于測試函數(shù)使用的每個?fixture
?,在測試函數(shù)的定義中通常都有一個參數(shù)(以?fixture
?命名)
在基本級別上,測試函數(shù)通過將??fixture
??聲明為參數(shù)來請求它們所需要的??fixture
??。
當pytest運行一個測試時,它會查看該測試函數(shù)簽名中的參數(shù),然后搜索與這些參數(shù)具有相同名稱的??fixture
??。一旦pytest找到它們,它就運行這些??fixture
??,捕獲它們返回的內(nèi)容(如果有的話),并將這些對象作為參數(shù)傳遞給測試函數(shù)。
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看到這個時,它將執(zhí)行??fruit_bowl fixture
??函數(shù),并將它返回的對象作為??fruit_bowl
??參數(shù)傳遞給??test_fruit_salad
??
如果我們手動進行,大致會發(fā)生以下情況:
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)
pytest最大的優(yōu)勢之一是它極其靈活的??fixture
??系統(tǒng)。它允許我們將測試的復雜需求歸結(jié)為更簡單和更有組織的功能,我們只需要讓每個功能描述它們所依賴的東西。我們將進一步深入討論這個問題,但現(xiàn)在,這里有一個快速的例子來演示??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
?就像測試一樣。 所有相同的請求規(guī)則都適用于用于測試的??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)
使pytest的??fixture
??系統(tǒng)如此強大的原因之一是,它使我們能夠定義一個通用的設置步驟,這個步驟可以重復使用,就像使用一個普通函數(shù)一樣。兩個不同的測試可以請求相同的??fixture
??,并讓pytest從該??fixture
??為每個測試提供自己的結(jié)果。
這對于確保測試不會相互影響非常有用。 我們可以使用這個系統(tǒng)來確保每個測試都獲得自己的新一批數(shù)據(jù),并從干凈的狀態(tài)開始,這樣它就可以提供一致的、可重復的結(jié)果。
下面是一個例子,說明這是如何派上用場的:
# 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
??被執(zhí)行兩次(??first_entry fixture
??也是如此)。如果我們手動執(zhí)行,它看起來會是這樣的:
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)
測試和??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
??也可以被請求多次,pytest不會為該測試再次執(zhí)行它們。這意味著我們可以請求多個依賴于它們的??fixture
??(甚至在測試本身中的??fixture
??),而不需要執(zhí)行多次這些??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
??在測試期間每次被請求時都被執(zhí)行一次,那么這個測試將會失敗,因為??append_first
??和??test_string_only
??都會將??order
??視為一個空列表,但由于??order
??的返回值在第一次被調(diào)用后被緩存(以及執(zhí)行它可能有的任何副作用),??test
??和??append_first
??都引用了同一個對象,測試中看到了??append_first
??對該對象的影響。
更多建議: