歷史記錄

此頁面列出多年來已變更的 pytest 先前版本功能或行為。它們保留在此處作為歷史記錄,以便查看舊程式碼的使用者可以找到與它們相關的文件。

標記改造和反覆

在版本 3.6 中變更。

pytest 的標記實作傳統上是透過單純更新函式的 __dict__ 屬性,以累積新增標記。結果是,標記會以令人驚訝的方式意外傳遞到類別階層中。此外,擷取它們的 API 不一致,因為參數化的標記會以不同於使用 @pytest.mark 裝飾器套用的標記,以及透過 node.add_marker 新增的標記的方式儲存。

這種狀態在技術上幾乎不可能正確使用來自標記的資料,而沒有深入了解內部結構,導致在更進階的使用中產生細微且難以理解的錯誤。

根據標記是如何宣告/變更,會取得 MarkerInfo,其中可能包含來自同層類別的標記、MarkDecorators(當標記來自參數化或 node.add_marker 呼叫時),捨棄先前的標記。此外,MarkerInfo 會像單一標記一樣作用,但實際上它表示對具有相同名稱的多個標記的合併檢視。

最重要的是,標記無法以相同的方式存取模組、類別和函式/方法。事實上,標記只能在函式中存取,即使它們宣告在類別/模組中。

在 pytest 3.6 中已導入新的 API 來存取標記,以解決初始設計的問題,提供 _pytest.nodes.Node.iter_markers() 方法以一致的方式反覆標記,並重新設計內部結構,解決了初始設計中的許多問題。

更新程式碼

舊的 Node.get_marker(name) 函式被視為已棄用,因為它會傳回內部 MarkerInfo 物件,其中包含套用至該節點的所有標記的合併名稱、*args**kwargs

一般來說,有兩種情境說明標記應如何處理

1. 標記會互相覆寫。順序很重要,但您只想將標記視為單一項目。例如,模組層級的 log_level('info') 可以被特定測試的 log_level('debug') 覆寫。

在這種情況下,請使用 Node.get_closest_marker(name)

# replace this:
marker = item.get_marker("log_level")
if marker:
    level = marker.args[0]

# by this:
marker = item.get_closest_marker("log_level")
if marker:
    level = marker.args[0]

2. 標記以加法方式組成。例如 skipif(condition) 標記表示您只想評估所有標記,順序並不重要。您可能想將您的標記視為這裡的一個集合。

在這種情況下,迭代每個標記並個別處理其 *args**kwargs

# replace this
skipif = item.get_marker("skipif")
if skipif:
    for condition in skipif.args:
        # eval condition
        ...

# by this:
for skipif in item.iter_markers("skipif"):
    condition = skipif.args[0]
    # eval condition

如果您不確定或有任何疑問,請考慮開啟 一個問題

快取外掛整合到核心

先前以 pytest-cache 為名的第三方外掛來散發 核心快取 外掛的功能。核心外掛與命令列選項和 API 使用方式相容,但您只能在測試執行之間儲存/接收可序列化為 JSON 的資料。

函式參數和 pytest_funcarg__

在 2.3 以前的版本中,沒有 @pytest.fixture 標記,您必須使用函式參數工廠的 pytest_funcarg__NAME 前綴。這仍然存在且會繼續支援,但不再宣傳為宣告函式參數功能的主要方式。

@pytest.yield_fixture 裝飾器

在 2.10 版本以前,為了使用 yield 陳述式來執行清除程式碼,必須使用 yield_fixture 標記來標記函式參數。從 2.10 開始,一般函式參數可以使用 yield,因此 yield_fixture 裝飾器不再需要,且被視為已棄用。

[pytest] 標頭在 setup.cfg

在 3.0 之前,支援的區段名稱為 [pytest]。由於這可能會與某些 distutils 指令衝突,setup.cfg 檔案推薦的區段名稱現在為 [tool:pytest]

請注意,對於 pytest.initox.ini 檔案,區段名稱為 [pytest]

將標記套用至 @pytest.mark.parametrize 參數

在 3.1 版之前,用於標記值的支援機制使用下列語法

import pytest


@pytest.mark.parametrize(
    "test_input,expected", [("3+5", 8), ("2+4", 6), pytest.mark.xfail(("6*9", 42))]
)
def test_eval(test_input, expected):
    assert eval(test_input) == expected

這是支援此功能的初始破解,但很快就被證明是不完整的,傳遞函數或套用具有相同名稱但不同參數的多個標記時會中斷。

舊語法計畫在 pytest-4.0 中移除。

@pytest.mark.parametrize 參數名稱作為元組

在 2.4 版之前的版本中,需要將參數名稱指定為元組。這仍然有效,但更簡單的 "name1,name2,..." 逗號分隔字串語法現在會優先宣傳,因為它比較容易撰寫且產生的程式碼雜訊較少。

setup:現在是「autouse 固定裝置」

在 pytest-2.3 發行前的開發期間,名稱 pytest.setup 已使用,但在發行前已重新命名並移至一般固定裝置機制的其中一部分,即 Autouse 固定裝置(您不必要求的固定裝置)

條件為字串,而非布林值

在 pytest-2.4 之前,指定 skipif/xfail 條件的唯一方法是使用字串

import sys


@pytest.mark.skipif("sys.version_info >= (3,3)")
def test_function(): ...

在測試函數設定期間,skipif 條件會透過呼叫 eval('sys.version_info >= (3,0)', namespace) 來評估。命名空間包含所有模組全域變數,以及至少 ossys

自 pytest-2.4 起,布林條件 被認為是較佳選擇,因為標記可以自由地匯入測試模組之間。使用字串時,您不僅需要匯入標記,還需要匯入標記使用的所有變數,這會違反封裝。

指定條件為字串的原因是 pytest 可以根據條件字串純粹報告跳過條件的摘要。使用布林條件時,您需要指定 reason 字串。

請注意,字串條件將保持完全支援,如果您不需要跨匯入標記,您可以自由使用它們。

pytest.mark.skipif(conditionstring)pytest.mark.xfail(conditionstring) 中對條件字串進行評估會在命名空間字典中進行,該字典的建構方式如下

  • 命名空間初始化時會放入 sysos 模組,以及 pytest config 物件。

  • 使用套用表達式的測試函數的模組全域變數進行更新。

pytest config 物件允許您根據您可能已新增的測試組態值進行跳過

@pytest.mark.skipif("not config.getvalue('db')")
def test_function(): ...

使用「布林條件」的等效方式為

@pytest.mark.skipif(not pytest.config.getvalue("db"), reason="--db was not specified")
def test_function():
    pass

注意

您無法在 pytest 的引數解析發生之前匯入的程式碼中使用 pytest.config.getvalue()。例如, conftest.py 檔案會在命令列解析之前匯入,因此 config.getvalue() 將無法正確執行。

pytest.set_trace()

在版本 2.4 之前,要在程式碼中設定中斷點,需要使用 pytest.set_trace()

import pytest


def test_function():
    ...
    pytest.set_trace()  # invoke PDB debugger and tracing

這不再需要,您可以直接使用本機 import pdb;pdb.set_trace() 呼叫。

更多詳細資訊,請參閱 設定中斷點

「相容性」屬性

透過 節點 實例存取 模組函式類別實例檔案項目 早已記錄為不建議使用,但從 pytest 3.9 開始會發出警告。

使用者應僅 匯入 pytest 並使用 pytest 模組存取這些物件。