pytest-2.3:fixture/funcarg 演進的原因¶
目標受眾:閱讀本文檔需要 Python 測試、xUnit 設定方法和 (先前) 基本 pytest funcarg 機制的基礎知識,請參閱 funcargs 和 pytest_funcarg__。如果您是 pytest 的新手,則可以忽略本節並閱讀其他章節。
先前 pytest_funcarg__
機制的缺點¶
在 pytest-2.3 之前的 funcarg 機制中,每次需要測試函數的 funcarg 時,都會調用工廠函數。如果工廠想要跨不同範圍重用資源,它通常使用 request.cached_setup()
輔助函數來管理資源的快取。以下是一個基本範例,說明我們如何實作每個 session 的 Database 物件
# 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” 資源並不直接:您需要套用 “parametrize” 裝飾器或實作呼叫
pytest_generate_tests
hook 的parametrize()
hook,該 hook 在資源使用的地方執行參數化。此外,您需要修改工廠以使用包含request.param
的extrakey
參數來呼叫Request.cached_setup
。多個參數化的 session 範圍資源將同時處於活動狀態,使得它們難以影響被測應用程式的全局狀態。
您無法在 xUnit 設定方法中使用 funcarg 工廠。
如果非參數化的 fixture 函數未在測試函數簽名中聲明,則無法使用參數化的 funcarg 資源。
所有這些限制都已在 pytest-2.3 及其改進的 fixture 機制 中得到解決。
直接設定 fixture/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()
,因為它每個 session 只會被調用一次。此外,request.addfinalizer()
會根據工廠函數運作的指定資源範圍註冊一個終結器。
直接參數化 funcarg 資源工廠¶
以前,funcarg 工廠無法直接引起參數化。您需要在測試函數上指定 @parametrize
裝飾器或實作 pytest_generate_tests
hook 來執行參數化,即使用不同的值集多次呼叫測試。pytest-2.3 引入了一個裝飾器,用於工廠本身
@pytest.fixture(params=["mysql", "pg"])
def db(request): ... # use request.param
在這裡,工廠將被調用兩次(分別使用設定為 request.param
屬性的 “mysql” 和 “pg” 值),並且所有需要 “db” 的測試也將執行兩次。“mysql” 和 “pg” 值也將用於報告測試調用變體。
這種新的參數化 funcarg 工廠的方式在許多情況下應該允許重用已編寫的工廠,因為當通過 metafunc.parametrize(indirect=True)
呼叫參數化測試函數/類別時,實際上已經使用了 request.param
。
當然,完全可以結合參數化和範圍設定
@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
這將執行所有需要每個 session “db” 資源的測試兩次,接收由工廠函數的兩個相應調用建立的值。
使用 @fixture 裝飾器時,沒有 pytest_funcarg__
前綴¶
使用 @fixture
裝飾器時,函數的名稱表示可以作為函數參數存取資源的名稱
@pytest.fixture()
def db(request): ...
可以請求 funcarg 資源的名稱是 db
。
您仍然可以使用指定 funcarg 工廠的 “舊” 非裝飾器方式,也就是
def pytest_funcarg__db(request): ...
但是這樣就無法定義範圍設定和參數化。因此,建議使用工廠裝飾器。
解決每個 session 的設定 / autouse fixtures¶
長期以來,pytest 提供了 pytest_configure 和 pytest_sessionstart hook,它們通常用於設定全局資源。這存在幾個問題
在分散式測試中,管理進程會設定永遠不需要的測試資源,因為它只協調 worker 進程的測試執行活動。
如果您只執行收集 (“–collect-only”),仍然會執行資源設定。
如果 pytest_sessionstart 包含在某些子目錄 conftest.py 檔案中,則不會調用它。這源於此 hook 實際上用於報告,特別是包含平台/自訂資訊的測試標頭。
此外,從外掛程式或 conftest 檔案中定義範圍設定並不容易,除非實作 pytest_runtest_setup()
hook 並自行處理範圍設定/快取。並且幾乎不可能通過參數化來完成此操作,因為 pytest_runtest_setup()
在測試執行期間調用,而參數化發生在收集時。
因此,pytest_configure/session/runtest_setup 通常不適合實作常見的 fixture 需求。因此,pytest-2.3 引入了 Autouse fixtures(您不必請求的 fixtures),它與通用的 fixture 機制 完全整合,並且淘汰了許多先前 pytest hook 的用法。
funcargs/fixture 發現現在在收集時發生¶
自 pytest-2.3 以來,fixture/funcarg 工廠的發現會在收集時處理。這對於大型測試套件來說更有效率。此外,調用 “pytest –collect-only” 在未來應該能夠顯示大量設定資訊,因此提供了一種很好的方法來了解專案中的 fixture 管理。
結論和相容性注意事項¶
funcargs 最初在 pytest-2.0 中引入。在 pytest-2.3 中,該機制得到了擴展和改進,現在被描述為 fixtures
以前,funcarg 工廠使用特殊的
pytest_funcarg__NAME
前綴指定,而不是使用@pytest.fixture
裝飾器。工廠接收到一個
request
物件,該物件通過request.cached_setup()
呼叫管理快取,並允許通過request.getfuncargvalue()
呼叫使用其他 funcargs。這些複雜的 API 使得難以進行適當的參數化和實作資源快取。新的pytest.fixture()
裝飾器允許宣告範圍,並讓 pytest 為您找出解決方案。如果您使用了參數化和 funcarg 工廠,並且使用了
request.cached_setup()
,建議您花費幾分鐘時間簡化您的 fixture 函數程式碼,以改用 Fixtures 參考 裝飾器。這也將允許您利用測試的自動每個資源分組。