ПРЕДЛОЖЕНИЕ: Параметризация с помощью приспособлений¶
Предупреждение
В этом документе изложено предложение по использованию приспособлений в качестве входных данных параметризованных тестов или приспособлений.
Проблема¶
Как пользователь, я имею функциональные тесты, которые я хотел бы проводить по различным сценариям.
В данном конкретном примере мы хотим создать новый проект на основе шаблона 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