pytest fixture-請求fixture

2022-03-23 14:33 更新

什么是fixture

在測試中,?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)

Fixtures可以請求其他fixtures

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)

Fixtures可重復使用

使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)

一個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不會為該測試再次執(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??對該對象的影響。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號