歷史記錄

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

標記改版和迭代

在版本 3.6 中變更。

pytest 的標記實作傳統上是透過簡單地更新函數的 __dict__ 屬性來累積新增標記。因此,標記會以令人驚訝的方式非預期地沿著類別層級結構傳遞。此外,用於檢索它們的 API 不一致,因為來自參數化的標記與使用 @pytest.mark 裝飾器新增的標記以及透過 node.add_marker 新增的標記儲存方式不同。

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

根據標記的宣告/變更方式,人們會獲得 MarkerInfo,其中可能包含來自同級類別的標記;當標記來自參數化或來自 node.add_marker 呼叫時,會獲得 MarkDecorators,並捨棄先前的標記。此外,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

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

快取插件整合到核心中

核心快取 插件的功能以前是作為名為 pytest-cache 的第三方插件發布的。核心插件在命令行選項和 API 用法方面是相容的,除了您只能在測試執行之間儲存/接收可 JSON 序列化的資料。

funcargs 和 pytest_funcarg__

在 2.3 之前的版本中,沒有 @pytest.fixture 標記,您必須為 fixture 工廠使用魔術 pytest_funcarg__NAME 前綴。這仍然保留並將繼續受到支援,但不再作為宣告 fixture 函數的主要方式宣傳。

@pytest.yield_fixture 裝飾器

在 2.10 之前的版本中,為了使用 yield 語句來執行 teardown 程式碼,必須使用 yield_fixture 標記來標記 fixture。從 2.10 開始,普通的 fixture 可以直接使用 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

這是支援該功能的初始 hack,但很快就被證明是不完整的,對於傳遞函數或套用具有相同名稱但不同參數的多個標記而言是損壞的。

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

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

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

setup: 現在是一個「自動使用 fixture」

在 pytest-2.3 發布之前的開發過程中,使用了名稱 pytest.setup,但在發布之前,它被重新命名並移動以成為通用 fixture 機制的一部分,即 自動使用 fixture (您無需請求的 fixture)

條件作為字串而不是布林值

在 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 可以僅根據條件字串報告 skip 條件的摘要。對於布林條件,您需要指定 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() 呼叫。

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

「相容性」屬性

透過 Node 實例存取 ModuleFunctionClassInstanceFileItem 長期以來一直被記錄為已棄用,但從 pytest 3.9 開始發出警告。

使用者應該只 import pytest 並使用 pytest 模組存取這些物件。