[英]Pytest - combining multiple fixture parameters into a single fixture to optimise fixture instantiation
我有一個現有的pytest
測試,它使用一些預定義的列表來測試它們的交叉產品:
A_ITEMS = [1, 2, 3]
B_ITEMS = [4, 5, 6]
C_ITEMS = [7, 8, 9]
我還有一個昂貴的夾具,其內部條件取決於 A 和 B 項目(但不是 C),稱為 F:
class Expensive:
def __init__(self):
# expensive set up
time.sleep(10)
def check(self, a, b, c):
return True # keep it simple, but in reality this depends on a, b and c
@pytest.fixture
def F():
return Expensive()
目前,我有一種簡單的方法,可以簡單地將測試 function 參數化:
@pytest.mark.parametrize("A", A_ITEMS)
@pytest.mark.parametrize("B", B_ITEMS)
@pytest.mark.parametrize("C", C_ITEMS)
def test_each(F, A, B, C):
assert F.check(A, B, C)
這將測試 F 與 A、B 和 C 項目的所有組合,但是它通過F
夾具為每個測試構造一個新的Expensive
實例。 更具體地說,它通過夾具 F 為 A、B 和 C 的每個組合重建一個新的Expensive
。
這是非常低效的,因為我只需要在 A 和 B 的值發生變化時構造一個新的Expensive
,而在 C 的所有測試之間它們不會。
我想做的是以某種方式將F
夾具與A_ITEMS
和B_ITEMS
列表結合起來,以便 F 夾具每次運行 C 的值時只實例化一個新實例一次。
我的第一種方法是將 A 和 B 列表分離到它們自己的固定裝置中,並將它們與 F 固定裝置結合起來:
class Expensive:
def __init__(self, A, B):
# expensive set up
self.A = A
self.B = B
time.sleep(10)
def check(self, c):
return True # keep it simple
@pytest.fixture(params=[1,2,3])
def A(request):
return request.param
@pytest.fixture(params=[4,5,6])
def B(request):
return request.param
@pytest.fixture
def F(A, B):
return Expensive(a, b)
@pytest.mark.parametrize("C", C_ITEMS)
def test_each2(F, C):
assert F.check(C)
盡管這會測試所有組合,但不幸的是,這會為每個測試創建一個新的Expensive
實例,而不是將每個 A 和 B 項目組合成一個可重復用於 C 值的單個實例。
我研究了間接裝置,但我看不到將多個列表(即 A 和 B 項)發送到單個裝置的方法。
我可以用 pytest 采取更好的方法嗎? 本質上,我想要做的是盡量減少實例化Expensive
的次數,因為它取決於項目 A 和 B 的值。
注意:我試圖簡化這一點,但實際情況是 F 表示創建一個新進程,A 和 B 是該進程的命令行參數,C 只是一個通過 a 傳遞給進程的值插座。 因此,我希望能夠將 C 的每個值發送到此進程,而無需在每次 C 更改時重新創建它,但顯然如果 A 或 B 更改,我需要重新啟動它(因為它們是進程的命令行參數)。
如果使用 pytest 范圍(如其他答案中所建議)不是一個選項,您可以嘗試緩存擴展的 object,以便僅在需要時構建它。
基本上,這通過額外的Expansive
緩存最后使用的參數來擴展問題中給出的建議,以避免在不需要時創建新的擴展:
@pytest.fixture(params=A_ITEMS)
def A(request):
return request.param
@pytest.fixture(params=B_ITEMS)
def B(request):
return request.param
class FFactory:
lastAB = None
lastF = None
@classmethod
def F(cls, A, B):
if (A, B) != cls.lastAB:
cls.lastAB = (A, B)
cls.lastF = Expensive(A, B)
return cls.lastF
@pytest.fixture
def F(A, B):
return FFactory.F(A, B)
@pytest.mark.parametrize("C", C_ITEMS)
def test_each(F, C):
assert F.check(C)
在這種情況下,我已經使用范圍更廣的夾具(模塊或會話)作為每個測試夾具的“緩存”取得了一些成功,這種情況下夾具的“壽命”與攤銷成本或任何。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.