ПРЕДЛОЖЕНИЕ: Параметризация с помощью приспособлений

Предупреждение

В этом документе изложено предложение по использованию приспособлений в качестве входных данных параметризованных тестов или приспособлений.

Проблема

Как пользователь, я имею функциональные тесты, которые я хотел бы проводить по различным сценариям.

В данном конкретном примере мы хотим создать новый проект на основе шаблона cookiecutter. Мы хотим протестировать значения по умолчанию, а также данные, эмулирующие пользовательский ввод.

  • использовать значения по умолчанию

  • эмулировать ввод данных пользователем

    • указать «автора

    • указать „project_slug“

    • указать „author“ и „project_slug“

Вот как может выглядеть функциональный тест:

import pytest


@pytest.fixture
def default_context():
    return {"extra_context": {}}


@pytest.fixture(
    params=[
        {"author": "alice"},
        {"project_slug": "helloworld"},
        {"author": "bob", "project_slug": "foobar"},
    ]
)
def extra_context(request):
    return {"extra_context": request.param}


@pytest.fixture(params=["default", "extra"])
def context(request):
    if request.param == "default":
        return request.getfuncargvalue("default_context")
    else:
        return request.getfuncargvalue("extra_context")


def test_generate_project(cookies, context):
    """Call the cookiecutter API to generate a new project from a
    template.
    """
    result = cookies.bake(extra_context=context)

    assert result.exit_code == 0
    assert result.exception is None
    assert result.project.isdir()

Вопросы

  • Используя request.getfuncargvalue(), мы полагаемся на фактическое выполнение функции приспособления, чтобы узнать, какие приспособления задействованы, из-за их динамической природы

  • Что еще более важно, request.getfuncargvalue() нельзя комбинировать с параметризованными фиксаторами, такими как extra_context.

  • Это очень неудобно, если вы хотите расширить существующий набор тестов определенными параметрами для приспособлений, которые уже используются тестами

pytest версии 3.0 сообщает об ошибке при попытке выполнить приведенный выше код:

Failed: The requested fixture has no parameter defined for the current
test.

Requested fixture 'extra_context'

Предлагаемое решение

Новая функция, которая может использоваться в модулях, может быть использована для динамического определения приспособлений из существующих.

pytest.define_combined_fixture(
    name="context", fixtures=["default_context", "extra_context"]
)

Новое приспособление context наследует область видимости от используемых приспособлений и дает следующие значения.

  • {}

  • {'author': 'alice'}

  • {'project_slug': 'helloworld'}

  • {'author': 'bob', 'project_slug': 'foobar'}

Альтернативный подход

Новая вспомогательная функция с именем fixture_request будет сообщать pytest о необходимости выдать все параметры, помеченные как фикстура.

Примечание

Плагин pytest-lazy-fixture реализует решение, очень похожее на предложение ниже, обязательно ознакомьтесь с ним.

@pytest.fixture(
    params=[
        pytest.fixture_request("default_context"),
        pytest.fixture_request("extra_context"),
    ]
)
def context(request):
    """Returns all values for ``default_context``, one-by-one before it
    does the same for ``extra_context``.

    request.param:
        - {}
        - {'author': 'alice'}
        - {'project_slug': 'helloworld'}
        - {'author': 'bob', 'project_slug': 'foobar'}
    """
    return request.param

Этот же помощник можно использовать в комбинации с pytest.mark.parametrize.

@pytest.mark.parametrize(
    "context, expected_response_code",
    [
        (pytest.fixture_request("default_context"), 0),
        (pytest.fixture_request("extra_context"), 0),
    ],
)
def test_generate_project(cookies, context, exit_code):
    """Call the cookiecutter API to generate a new project from a
    template.
    """
    result = cookies.bake(extra_context=context)

    assert result.exit_code == exit_code
Вернуться на верх