Как параметризовать с pytest над декартовым произведением параметров, некоторые фиксированные? - PullRequest
1 голос
/ 28 апреля 2019

Я пишу тесты для базового класса и его подклассов. Это выглядит примерно так:

@pytest.fixture()
def bc()
    return BaseClass(...)

@pytest.mark.parametrize('param1,param2', [...])
def test_baseclass_func1(bc, param1, param2):
    assert ...

@pytest.mark.parametrize('param1,param2', [...])
def test_baseclass_func2(bc, param1, param2):
    assert ...

@pytest.mark.parametrize('param1,param2', [xyz])
def test_baseclass_func3(bc, param1, param2):
    assert ...

@pytest.mark.parametrize('param1,param2', [xyz])
def test_subclass1_func3(param1, param2):
    sc1 = SubClass1(...)
    assert ...

@pytest.mark.parametrize('param1,param2', [xyz])
def test_subclass2_func3(param1, param2):
    sc1 = SubClass2(...)
    assert ...

Итак, я хочу использовать фиксированный экземпляр BaseClass, у меня нет очевидной причины использовать фикстуру над подклассами, и я хочу избежать трехкратного копирования-вставки теста для func3.
Мой лучший способ - использовать еще одну параметризацию:

@pytest.mark.parametrize('classtype', [
        (BaseClass,),
        (SubClass1,),
        (SubClass2,),
])
@pytest.mark.parametrize('param1,param2', [xyz])
def test_func3(classtype, param1, param2):
    class_inst = classtype()
    assert ...

но поскольку для этого требуется дополнительный экземпляр BaseClass, я думаю, что есть лучшее решение. Какой трюк мне не хватает?

Ответы [ 2 ]

2 голосов
/ 29 апреля 2019

Если я правильно понимаю, вы можете использовать косвенную параметризацию для преобразования аргументов класса в экземпляры, передаваемые в тест:

@pytest.fixture(scope='session')
def instance(request):
    cls = request.param
    instance = cls()
    yield instance
    # cleanup instance here if necessary

@pytest.mark.parametrize('instance', (BaseClass, SubClass1, SubClass2,), indirect=True)
@pytest.mark.parametrize('param1,param2', (('spam', 'foo'), ('eggs', 'bar')))
def test_func3(instance, param1, param2):
    assert instance.func3(param1, param2) == 'bacon'

Поскольку прибор instance находится в области сеансаВсе созданные экземпляры создаются один раз и повторно используются в течение всего сеанса тестирования.Если вам нужно более сложное создание экземпляра, вы можете сделать это в приборе, сохраняя тесты чистыми.Пример с повторным использованием прибора bc:

@pytest.fixture(scope='session')
def bc():
    return BaseClass(...)

@pytest.fixture(scope='session')
def instance(request, bc):
    cls = request.param
    if cls.__name__ == 'BaseClass':
        instance = bc
    else:
        instance = cls()
    yield instance
1 голос
/ 29 апреля 2019

Возможно, вас заинтересует такой прибор, который не создаст экземпляр BaseClass, пока он не будет вызван:

@pytest.fixture
def callable_bc():
    def _bc():
        return BaseClass(...)
    return _bc

@pytest.mark.parametrize('classtype', [
        (BaseClass,),
        (SubClass1,),
        (SubClass2,),
])
@pytest.mark.parametrize('param1,param2', [xyz])
def test_func3(classtype, param1, param2, callable_bc):
    class_inst = classtype() if classtype != BaseClass else callable_bc()
    assert ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...