簡體   English   中英

Pytest - 將多個夾具參數組合到單個夾具中以優化夾具實例化

[英]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_ITEMSB_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM