简短的回答是pytest doesn't support passing fixtures to parametrize。
pytest 提供的开箱即用解决方案是使用indirect parametrization 或使用pytest_generate_tests 定义您自己的参数化方案,如How can I pass fixtures to pytest.mark.parameterize? 中所述
这些是我以前用来解决此问题的解决方法。
选项 1:生成values 的单独函数
from typing import Iterator, List
import pytest
def generate_values() -> Iterator[str]:
# ... some computationally-intensive operation ...
all_possible_values = [1, 2, 3]
for value in all_possible_values:
yield value
@pytest.fixture()
def values() -> List[str]:
return list(generate_values())
def test_all_values(values):
assert len(values) > 5
@pytest.mark.parametrize("value", generate_values())
def test_one_value_at_a_time(value: int):
assert value == 999
$ pytest -vv tests
...
========================================================== short test summary info ===========================================================
FAILED tests/test_main.py::test_all_values - assert 3 > 5
FAILED tests/test_main.py::test_one_value_at_a_time[1] - assert 1 == 999
FAILED tests/test_main.py::test_one_value_at_a_time[2] - assert 2 == 999
FAILED tests/test_main.py::test_one_value_at_a_time[3] - assert 3 == 999
主要变化是将值列表的生成移动到常规的非固定函数generate_values。如果它是一个静态列表,那么您甚至可以放弃将其设为函数,而只需将其定义为常规模块级变量。
ALL_POSSIBLE_VALUES = [1, 2, 3]
并非所有东西都需要固定。将测试数据注入函数是有利的,是的,但这并不意味着您不能使用常规的 Python 函数和变量。此解决方案的唯一问题是生成值列表是否依赖于其他装置,即reusable fixtures。在这种情况下,您也必须重新定义它们。
我在这里保留了values 固定装置,用于您需要进行的测试全部可能的值作为一个列表,如test_all_values。
如果这个值列表将用于多个其他测试,而不是用parametrize 装饰每个测试,您可以在pytest_generate_tests 钩子中执行此操作。
def pytest_generate_tests(metafunc: pytest.Metafunc):
if "value" in metafunc.fixturenames:
metafunc.parametrize("value", generate_values())
def test_one_value_at_a_time(value: int):
assert value == 999
这个选项避免了很多重复,然后您甚至可以将generate_values 更改为您需要的任何内容,或者您需要它独立于测试和测试框架。
选项 2:使用 indirect 并让夹具一次返回 1 个值
如果有可能知道值列表的长度预先(就像在运行测试之前一样),然后您可以使用parametrize 的indirect=,然后让夹具一次只返回一个值。
# Set/Defined by some env/test configuration?
MAX_SUPPORTED_VALUES = 5
@pytest.fixture
def value(request: pytest.FixtureRequest) -> int:
all_possible_values = [1, 2, 3, 4, 5]
selected_index = request.param
return all_possible_values[selected_index]
@pytest.mark.parametrize("value", range(MAX_SUPPORTED_VALUES), indirect=True)
def test_one_value_at_a_time(value: int):
assert value == 999
主要的变化是让夹具接受一个参数(一个索引),然后返回该索引处的值。 (还将夹具从 values 重命名为 value 以匹配返回值)。然后,在测试中,您使用indirect=True,然后传递一系列索引,这些索引以request.param 的形式传递给fixture。
同样,这仅在您至少知道值列表的长度时才有效。
同样,您可以使用pytest_generate_tests,而不是为使用此夹具的每个测试应用parametrize 装饰器:
def pytest_generate_tests(metafunc: pytest.Metafunc):
if "value" in metafunc.fixturenames:
metafunc.parametrize("value", range(MAX_SUPPORTED_VALUES), indirect=True)
def test_one_value_at_a_time(value: int):
assert value == 999