Pytest - объединение нескольких параметров прибора в один прибор для оптимизации его реализации - PullRequest
3 голосов
/ 09 апреля 2020

У меня есть существующий тест 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()

В настоящее время у меня есть наивный подход, который просто параметризует тестовую функцию:

@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, однако он создает новый экземпляр Expensive через приспособление F для каждого теста . В частности, он восстанавливает новый Expensive через приспособление F для каждой комбинации A, B и C.

Это очень неэффективно, потому что мне нужно только построить новый Expensive, когда значения A и B меняются, что они не делают между всеми тестами 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) в один Fixtu re.

Есть ли лучший подход, который я могу использовать с pytest? По сути, я стремлюсь минимизировать количество экземпляров Expensive, учитывая, что оно зависит от значений элементов A и B.

Примечание. Я попытался упростить это, однако в реальной жизни F представляет собой создание нового процесса, A и B - параметры командной строки для этого процесса, а C - это просто значение, передаваемое процессу через сокет. Поэтому я хочу иметь возможность отправлять каждое значение C в этот процесс, не воссоздавая его каждый раз, когда изменяется C, но, очевидно, если A или B меняются, мне нужно перезапустить его (так как они являются параметрами командной строки для процесс).

Ответы [ 2 ]

0 голосов
/ 09 апреля 2020

У меня был некоторый успех при использовании осветительного прибора с более широкой областью действия (модуля или сеанса) в качестве «кэша» для осветительного прибора на тест для такой ситуации, когда «время жизни» самих осветителей не совпадает точно с амортизированные расходы или что-то еще.

0 голосов
/ 09 апреля 2020

Если использование pytest scoping (как предложено в другом ответе) не вариант, вы можете попытаться кэшировать экспансивный объект, так что он будет построен только при необходимости.

По сути, это расширяет предложение, приведенное в вопросе, с дополнительным кэшированием stati c последних использованных параметров, чтобы избежать создания нового 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)
...