關於 fixtures

pytest fixtures 旨在明確、模組化和可擴展。

什麼是 fixtures

在測試中,fixture 為測試提供已定義、可靠且一致的上下文。這可能包括環境(例如,配置了已知參數的資料庫)或內容(例如,資料集)。

Fixtures 定義了構成測試安排階段的步驟和資料(請參閱 測試的結構)。在 pytest 中,它們是您定義的用於此目的的函數。它們也可以用於定義測試的執行階段;這是一種設計更複雜測試的強大技術。

由 fixtures 建立的服務、狀態或其他操作環境通過參數由測試函數存取。對於測試函數使用的每個 fixture,在測試函數的定義中通常都有一個參數(以 fixture 命名)。

我們可以通過使用 @pytest.fixture 裝飾器來告訴 pytest 特定函數是 fixture。以下是 pytest 中 fixture 可能看起來像的簡單範例

import pytest


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

    def __eq__(self, other):
        return self.name == other.name


@pytest.fixture
def my_fruit():
    return Fruit("apple")


@pytest.fixture
def fruit_basket(my_fruit):
    return [Fruit("banana"), my_fruit]


def test_my_fruit_in_basket(my_fruit, fruit_basket):
    assert my_fruit in fruit_basket

測試也不必僅限於單個 fixture。它們可以依賴您想要的任意數量的 fixtures,並且 fixtures 也可以使用其他 fixtures。這就是 pytest 的 fixture 系統真正閃耀的地方。

相較於 xUnit 風格 setup/teardown 函數的改進

pytest fixtures 相較於經典的 xUnit 風格 setup/teardown 函數提供了顯著的改進

  • fixtures 具有明確的名稱,並通過從測試函數、模組、類別或整個專案宣告其使用來啟動。

  • fixtures 以模組化方式實作,因為每個 fixture 名稱都會觸發一個fixture 函數,該函數本身可以使用其他 fixtures。

  • fixture 管理範圍從簡單的單元測試到複雜的功能測試,允許根據配置和組件選項參數化 fixtures 和測試,或在函數、類別、模組或整個測試會話範圍內重複使用 fixtures。

  • 可以輕鬆且安全地管理 teardown 邏輯,無論使用了多少 fixtures,而無需手動仔細處理錯誤或微觀管理添加清理步驟的順序。

此外,pytest 繼續支援 如何實作 xunit 風格的設定。您可以混合使用這兩種風格,根據您的喜好從經典風格逐步轉向新風格。您也可以從現有的 unittest.TestCase 風格 開始。

Fixture 錯誤

pytest 會盡力將給定測試的所有 fixtures 放在線性順序中,以便它可以查看哪個 fixture 首先、第二、第三等等。但是,如果較早的 fixture 出現問題並引發異常,pytest 將停止執行該測試的 fixtures,並將該測試標記為發生錯誤。

當測試被標記為發生錯誤時,並不意味著測試失敗了。這僅表示由於測試依賴的某個事物出現問題,因此甚至無法嘗試進行測試。

這就是為什麼盡可能減少給定測試的不必要依賴關係是個好主意的原因之一。這樣,不相關的事物出現問題就不會導致我們對可能存在或可能不存在問題的事物產生不完整的畫面。

這是一個快速範例,有助於解釋

import pytest


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


@pytest.fixture
def append_first(order):
    order.append(1)


@pytest.fixture
def append_second(order, append_first):
    order.extend([2])


@pytest.fixture(autouse=True)
def append_third(order, append_second):
    order += [3]


def test_order(order):
    assert order == [1, 2, 3]

如果由於任何原因,order.append(1) 有錯誤並引發異常,我們將無法知道 order.extend([2])order += [3] 是否也會出現問題。在 append_first 拋出異常後,pytest 將不會為 test_order 運行更多 fixtures,甚至不會嘗試運行 test_order 本身。唯一會運行的將是 orderappend_first

共享測試資料

如果您想讓檔案中的測試資料可供您的測試使用,一種好的方法是在 fixture 中載入這些資料以供您的測試使用。這利用了 pytest 的自動快取機制。

另一種好的方法是在 tests 資料夾中新增資料檔案。還有社群插件可用於幫助管理測試的這一方面,例如 pytest-datadirpytest-datafiles

關於 fixture 清理的注意事項

pytest 不會對 SIGTERMSIGQUIT 信號執行任何特殊處理(SIGINT 由 Python 運行時通過 KeyboardInterrupt 自然處理),因此,管理外部資源的 fixtures 在 Python 進程被(這些信號)終止時,若這些外部資源需要被清除,則可能會洩漏資源。

pytest 不處理這些信號以執行 fixture 清理的原因是信號處理程序是全域的,並且更改它們可能會干擾正在執行的代碼。

如果您的套件中的 fixtures 在這些情況下需要特別注意終止,請參閱問題追蹤器中的 此評論,以獲取可能的解決方法。