过滤pytest装置 [英] Filtering pytest fixtures
问题描述
这与较旧的问题基本相同,但希望现在有更好的解决方案.
This is basically the same as an older question but hoping there is a better solution now.
问题:给定一个参数化的fixture,如何用fixtures对象的子集参数化一个测试函数?
The problem: Given a parametrized fixture, how can one parametrize a test function with a subset of the fixtures objects?
示例:
import pytest
@pytest.fixture(params=range(7))
def square(request):
return request.param ** 2
def test_all_squares(square):
sqrt = square ** 0.5
assert sqrt == int(sqrt)
@pytest.fixture()
def odd_square(square):
if square % 2 == 1:
return square
pytest.skip()
def test_all_odd_squares(odd_square):
assert odd_square % 2 == 1
sqrt = odd_square ** 0.5
assert sqrt == int(sqrt)
输出:
$ pytest pytests.py -v
=================================================================== test session starts ===================================================================
...
collected 14 items
pytests.py::test_all_squares[0] PASSED [ 7%]
pytests.py::test_all_squares[1] PASSED [ 14%]
pytests.py::test_all_squares[2] PASSED [ 21%]
pytests.py::test_all_squares[3] PASSED [ 28%]
pytests.py::test_all_squares[4] PASSED [ 35%]
pytests.py::test_all_squares[5] PASSED [ 42%]
pytests.py::test_all_squares[6] PASSED [ 50%]
pytests.py::test_all_odd_squares[0] SKIPPED [ 57%]
pytests.py::test_all_odd_squares[1] PASSED [ 64%]
pytests.py::test_all_odd_squares[2] SKIPPED [ 71%]
pytests.py::test_all_odd_squares[3] PASSED [ 78%]
pytests.py::test_all_odd_squares[4] SKIPPED [ 85%]
pytests.py::test_all_odd_squares[5] PASSED [ 92%]
pytests.py::test_all_odd_squares[6] SKIPPED [100%]
============================================================== 10 passed, 4 skipped in 0.02s ==============================================================
虽然这有效,但并不理想:
Although this works, it's not ideal:
- 它创建了总是被跳过的虚拟测试用例
- 它需要使用过滤装置的测试函数为参数提供不同的名称(
odd_square
).
我想要的是例如一个 filter_fixture(predicate, fixture)
函数,它过滤原始装置的对象并且可以传递给 pytest.mark.parametrize
,像这样:
What I'd like instead is e.g. a filter_fixture(predicate, fixture)
function that filters the original fixture's objects and can be passed to pytest.mark.parametrize
, something like this:
@pytest.mark.parametrize("square", filter_fixture(lambda x: x % 2 == 1, square))
def test_all_odd_squares(square):
assert square % 2 == 1
sqrt = square ** 0.5
assert sqrt == int(sqrt)
这是否可行?
推荐答案
如果您需要一定程度的逻辑来确定将哪些参数应用于每个测试,您可能需要考虑使用 pytest_generate_tests
钩子.
If you require a certain degree of logic to determine which parameters to apply to each test, you might want to consider using the pytest_generate_tests
hook.
钩子函数 pytest_generate_tests
为每个收集到的测试调用.metafunc
参数允许您动态地参数化每个单独的测试用例.重写您的示例以使用 pytest_generate_tests
可能如下所示:
The hook function pytest_generate_tests
is called for every collected test. The metafunc
argument allows you to dynamically parametrize each individual test case. Rewriting your example to use pytest_generate_tests
could look like this:
def pytest_generate_tests(metafunc):
square_parameters = (x**2 for x in range(7))
if 'square' in metafunc.fixturenames:
metafunc.parametrize("square", square_parameters)
if 'odd_square' in metafunc.fixturenames:
odd_square_parameters = (x for x in square_parameters if x % 2 == 1)
metafunc.parametrize("odd_square", odd_square_parameters)
def test_all_squares(square):
sqrt = square ** 0.5
assert sqrt == int(sqrt)
def test_all_odd_squares(odd_square):
assert odd_square % 2 == 1
sqrt = odd_square ** 0.5
assert sqrt == int(sqrt)
这会导致运行以下测试用例:
This results in the following test cases being run:
$ pytest -v pytests.py
=========== test session starts ===========
…
collected 10 items
pytests.py::test_all_squares[0] PASSED [ 10%]
pytests.py::test_all_squares[1] PASSED [ 20%]
pytests.py::test_all_squares[4] PASSED [ 30%]
pytests.py::test_all_squares[9] PASSED [ 40%]
pytests.py::test_all_squares[16] PASSED [ 50%]
pytests.py::test_all_squares[25] PASSED [ 60%]
pytests.py::test_all_squares[36] PASSED [ 70%]
pytests.py::test_all_odd_squares[1] PASSED [ 80%]
pytests.py::test_all_odd_squares[9] PASSED [ 90%]
pytests.py::test_all_odd_squares[25] PASSED [100%]
=========== 10 passed in 0.03s ===========
请注意,我示例中的测试 ID 与您的略有不同.但是,您可以使用 metafunc.parametrize
的 ìds
参数提供显式测试标识符.
Note that the test IDs in my example are slightly different from yours. However, you can provide explicit test identifiers using the ìds
argument of metafunc.parametrize
.
这篇关于过滤pytest装置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!