首页 > 解决方案 > 过滤 pytest 固定装置

问题描述

这与旧问题基本相同,但希望现在有更好的解决方案。

问题:给定一个参数化的夹具,如何使用夹具对象的子集参数化测试功能?

示例

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
 =========================================== ========================= 测试会话开始 ======================= ============================================
...
收集了 14 个项目                                                                                                                                        

pytests.py::test_all_squares[0] 通过 [7%]
pytests.py::test_all_squares[1] 通过 [14%]
pytests.py::test_all_squares[2] 通过 [21%]
pytests.py::test_all_squares[3] 通过 [28%]
pytests.py::test_all_squares[4] 通过 [35%]
pytests.py::test_all_squares[5] 通过 [42%]
pytests.py::test_all_squares[6] 通过 [50%]
pytests.py::test_all_odd_squares[0] 跳过 [57%]
pytests.py::test_all_odd_squares[1] 通过 [64%]
pytests.py::test_all_odd_squares[2] 跳过 [71%]
pytests.py::test_all_odd_squares[3] 通过 [78%]
pytests.py::test_all_odd_squares[4] 跳过 [85%]
pytests.py::test_all_odd_squares[5] 通过 [92%]
pytests.py::test_all_odd_squares[6] 跳过 [100%]

==================================================== ============ 10 次通过,4 次在 0.02 秒内跳过 ============================== =================================

虽然这可行,但并不理想:

我想要的是例如filter_fixture(predicate, fixture)过滤原始夹具对象并可以传递给的函数,pytest.mark.parametrize如下所示:

@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)

这在某种程度上可行吗?

标签: pythonpytestfixtures

解决方案


如果您需要一定程度的逻辑来确定将哪些参数应用于每个测试,您可能需要考虑使用pytest_generate_tests 钩子。

pytest_generate_tests为每个收集的测试调用钩子函数。该metafunc参数允许您动态参数化每个单独的测试用例。重写要使用的示例pytest_generate_tests可能如下所示:

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)

这将导致运行以下测试用例:

$ 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 与您的略有不同。ìds但是,您可以使用 的参数提供显式测试标识符metafunc.parametrize


推荐阅读