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