提案:使用夹具进行参数化¶
警告
本文档概述了一项提案,内容涉及使用夹具作为参数化测试或夹具的输入。
问题¶
作为用户,我有一些功能测试,希望针对各种场景运行它们。
在这个特定示例中,我们希望基于 cookiecutter 模板生成一个新项目。我们希望测试默认值,也测试模拟用户输入的数据。
使用默认值
模拟用户输入
指定“author”
指定“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