pytest-2.3:fixture/funcarg 演進的理由¶
目標受眾:閱讀此文件需要具備 python 測試、xUnit 設定方法和(以前的)基本 pytest funcarg 機制的基礎知識,請參閱 funcargs 和 pytest_funcarg__。如果您是 pytest 的新手,則可以忽略此部分,直接閱讀其他部分。
先前 pytest_funcarg__
機制的缺點¶
在 pytest-2.3 之前的 funcarg 機制會在每次測試函數需要 funcarg 時呼叫工廠。如果工廠想要在不同範圍內重複使用資源,則通常會使用 request.cached_setup()
輔助函數來管理資源快取。以下是我們如何實作每個工作階段資料庫物件的基本範例
# content of conftest.py
class Database:
def __init__(self):
print("database instance created")
def destroy(self):
print("database instance destroyed")
def pytest_funcarg__db(request):
return request.cached_setup(
setup=DataBase, teardown=lambda db: db.destroy, scope="session"
)
這種方法有幾個限制和困難
設定 funcarg 資源的範圍並不簡單,反而必須了解複雜的 cached_setup() 方法機制。
設定「db」資源的參數並不簡單:您需要套用「參數化」裝飾器或實作
pytest_generate_tests
鉤子,呼叫parametrize()
,在使用資源的地方執行參數化。此外,您需要修改工廠,使用包含request.param
的extrakey
參數,呼叫Request.cached_setup
。多個參數化的工作階段範圍資源會同時作用,讓它們難以影響受測應用程式的全域狀態。
您無法在 xUnit 設定方法中使用 funcarg 工廠。
如果未在測試函數簽章中宣告,非參數化的固定函數無法使用參數化的 funcarg 資源。
pytest-2.3 和其改良的 固定機制 解決了所有這些限制。
固定/funcarg 工廠的直接範圍¶
您可以使用 @pytest.fixture 裝飾器,直接宣告範圍,而不是使用快取範圍呼叫 cached_setup()
@pytest.fixture(scope="session")
def db(request):
# factory will only be invoked once per session -
db = DataBase()
request.addfinalizer(db.destroy) # destroy when session is finished
return db
此工廠實作不再需要呼叫 cached_setup()
,因為它只會在每個工作階段呼叫一次。此外,request.addfinalizer()
會根據工廠函式運作的指定資源範圍註冊一個終結器。
funcarg 資源工廠的直接參數化¶
先前,funcarg 工廠無法直接造成參數化。您需要在測試函式上指定 @parametrize
裝飾器,或實作 pytest_generate_tests
鉤子來執行參數化,亦即以不同的值集合多次呼叫測試。pytest-2.3 為工廠本身引入了裝飾器
@pytest.fixture(params=["mysql", "pg"])
def db(request): ... # use request.param
在此,工廠將被呼叫兩次(分別將「mysql」和「pg」值設定為 request.param
屬性),而所有需要「db」的測試也會執行兩次。「mysql」和「pg」值也會用於報告測試呼叫變異。
這種參數化 funcarg 工廠的新方法在許多情況下允許重複使用已寫入的工廠,因為實際上 request.param
已在透過 metafunc.parametrize(indirect=True)
呼叫參數化測試函式/類別時使用。
當然,參數化和範圍設定結合起來是完全沒問題的
@pytest.fixture(scope="session", params=["mysql", "pg"])
def db(request):
if request.param == "mysql":
db = MySQL()
elif request.param == "pg":
db = PG()
request.addfinalizer(db.destroy) # destroy when session is finished
return db
這會執行所有需要每個工作階段「db」資源的測試兩次,接收工廠函式兩個各自呼叫所建立的值。
使用 @fixture 裝飾器時沒有 pytest_funcarg__
前綴¶
使用 @fixture
裝飾器時,函式的名稱表示資源可作為函式引數存取的名稱
@pytest.fixture()
def db(request): ...
可要求 funcarg 資源的名稱是 db
。
您仍然可以使用「舊」的非裝飾器方式來指定 funcarg 工廠,又稱
def pytest_funcarg__db(request): ...
但這樣就無法定義範圍設定和參數化。因此建議使用工廠裝飾器。
解決每個工作階段設定/autouse 固定裝置¶
pytest 長期提供 pytest_configure 和 pytest_sessionstart 鉤子,它們經常被用於設定全域資源。這會產生幾個問題
在分散式測試中,管理程序會設定永遠不需要的測試資源,因為它只協調工作程序的測試執行活動。
如果你僅執行收集(使用「–collect-only」),資源設定仍會執行。
如果 pytest_sessionstart 包含在某些子目錄 conftest.py 檔案中,它將不會被呼叫。這是因為此掛勾實際上用於報告,特別是包含平台/自訂資訊的測試標頭。
此外,除了實作 pytest_runtest_setup()
掛勾並自行處理範圍/快取之外,很難從外掛程式或 conftest 檔案定義範圍設定。而且,由於 pytest_runtest_setup()
是在測試執行期間呼叫,而參數化發生在收集時間,因此幾乎不可能使用參數化來執行此操作。
因此,pytest_configure/session/runtest_setup 通常不適合用於實作常見的固定元件需求。因此,pytest-2.3 引入了 自動使用固定元件(不需要請求的固定元件),它與一般 固定元件機制 完全整合,並淘汰了許多先前使用 pytest 掛勾的方式。
函數參數/固定元件偵測現在在收集時間發生¶
從 pytest-2.3 開始,固定元件/函數參數工廠的偵測在收集時間處理。這對於大型測試套件來說更有效率。此外,呼叫「pytest –collect-only」應能在未來顯示許多設定資訊,因此提供一個不錯的方法來瞭解專案中的固定元件管理概況。
結論和相容性注意事項¶
函數參數最初是在 pytest-2.0 中引入的。在 pytest-2.3 中,此機制已擴充和改良,現在稱為固定元件
先前函數參數工廠使用特殊
pytest_funcarg__NAME
前置詞來指定,而不是使用@pytest.fixture
裝飾器。工廠收到一個
request
物件,它透過request.cached_setup()
呼叫來管理快取,並允許透過request.getfuncargvalue()
呼叫來使用其他函數參數。這些複雜的 API 讓正確的參數化和實作資源快取變得困難。新的pytest.fixture()
裝飾器允許宣告範圍,並讓 pytest 為你找出問題。如果您使用參數化和 funcarg 工廠,而這些工廠使用
request.cached_setup()
,建議花幾分鐘時間簡化您的固定函數程式碼,改用 固定參照 裝飾器。這也會讓您利用測試的自動化每個資源分組。