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