【问题标题】:how to pass multiple list arguments by command line to multiple pytest fixtures如何通过命令行将多个列表参数传递给多个 pytest 夹具
【发布时间】:2020-08-29 02:40:18
【问题描述】:

我想将下面的列表从我的命令行传递给我的 pytest 代码(如 python 中的 argparser),谁能帮助我如何做到这一点?

pytest -vs test_sample.py --html=results.html --A_list=['A1', 'A2'] --B_list=['B1'] --C_list=['C1', 'C2']

这是我的 pytest 代码。

import pytest

@pytest.fixture(scope="session", params=A_list) # here A_list is a list and it has to be passed from command line
def get_A_list(request):
    print("inside fixture get_A_list - element in A_list: ".format(request.param))
    yield request.param


@pytest.fixture(scope="module", params=B_list) # here B_list is a list and it has to be passed from command line
def get_B_list(request, get_A_list):
    print("inside fixture get_B_list - element in A_list: {}, element in B_list: {}: ".format(get_A_list, request.param))
    yield request.param


@pytest.mark.parametrize("c", C_list) # here C_list is a list and it has to be passed from command line
def test_functionality(get_A_list, get_B_list, c):
    print("inside function test_functionality - element in A_list: {}, element in B_list: {}, element in C_list: {}: ".format(get_A_list, get_B_list, c))

test_functionality 是我的测试用例函数。测试用例的数量应该基于命令行中传递的列表的长度

对于上述列表,它必须按以下顺序执行测试用例。

test_functionality[A1-B1-C1]
test_functionality[A1-B1-C2]
test_functionality[A2-B1-C1]
test_functionality[A2-B1-C2]

我需要使用 A_list 中的元素在 **fixture get_A_list 中执行一些操作 ....类似地,我需要使用 A_list 和 B_list 中的元素在fixture get_B_list 中执行一些操作 .......像这样我有 4 个夹具,最后我需要在 test_functionality 中执行测试用例,这将使用 A_list 、 B_list 、 C_list 等中的元素。

最后输出应该如下所示..

    inside fixture get_A_list - element in A_list: A1
    inside fixture get_B_list - element in A_list: A1, element in B_list: B1
    inside function test_functionality - element in A_list: A1, element in B_list: B1, element in C_list: C1
    inside function test_functionality - element in A_list: A1, element in B_list: B1, element in C_list: C2
    inside function test_functionality - element in A_list: A1, element in B_list: B1, element in C_list: C3
    inside fixture get_A_list - element in A_list: A2
    inside fixture get_B_list - element in A_list: A2, element in B_list: B1
    inside function test_functionality - element in A_list: A2, element in B_list: B1, element in C_list: C1
    inside function test_functionality - element in A_list: A2, element in B_list: B1, element in C_list: C2
    inside function test_functionality - element in A_list: A2, element in B_list: B1, element in C_list: C3

【问题讨论】:

    标签: python python-3.x pytest


    【解决方案1】:

    这里的问题是 params 参数是在加载时读取的,此时有关命令行参数的信息尚不可用,因此您不能像在示例中那样设置它们。

    最好的方法可能是重组你的代码,这样就不需要了。如果您能够做到这一点,那么您就不需要额外的固定装置,

    无论如何,你首先必须注册你的命令行参数:

    conftest.py

    def pytest_addoption(parser):
        parser.addoption("--A_list", action="store")
        parser.addoption("--B_list", action="store")
        parser.addoption("--C_list", action="store")
    

    现在,如果您可以重构您的代码,使固定装置不依赖于参数,您可以根据传递的命令行参数对您的测试进行参数化。如果您将参数作为逗号分隔的列表传递,您可以执行以下操作:

    test.py

    import pytest
    
    @pytest.hookimpl
    def pytest_generate_tests(metafunc):
        # collect fixture names with values
        option_values = {
            "a": metafunc.config.option.A_list,
            "b": metafunc.config.option.B_list,
            "c": metafunc.config.option.C_list
        }
    
        # add parametrization for each fixture name
        for name, value in option_values.items():
            if name in metafunc.fixturenames and value is not None:
                metafunc.parametrize(name, value.split(","))
    
    def test_functionality1(a, b, c):
        print(a, b, c)
    

    这将按所需顺序创建参数化测试。

    如果这不可行,您可以尝试在运行时创建您的固定装置(请参阅this post 了解更多信息):

    conftest.py

    # define the functions used as fixtures
    def a_list_fixture(request):
        print(f"get_A_list: {request.param}")
        yield request.param
    
    def b_list_fixture(request, get_A_list):
        print(f"get_B_list: {get_A_list},  {request.param}")
        yield request.param
    
    # at session start, create the fixtures with the correct params
    def pytest_sessionstart(session):
        a_list = session.config.option.A_list
        if a_list:
            a_list = a_list.split(",")
            name = "get_A_list"
            fn = pytest.fixture(scope='session', name=name,
                                params=a_list)(a_list_fixture)
            setattr(sys.modules[__name__], "{}_func".format(name), fn)
        b_list = session.config.option.B_list
        if b_list:
            b_list = b_list.split(",")
            name = "get_B_list"
            fn = pytest.fixture(scope='session', name=name,
                                params=b_list)(b_list_fixture)
            setattr(sys.modules[__name__], "{}_func".format(name), fn)
    

    请注意,您仍然需要为c 参数化函数,如上:

    test.py

    import pytest
    
    @pytest.hookimpl
    def pytest_generate_tests(metafunc):
        c_value = metafunc.config.option.C_list
        if "c" in metafunc.fixturenames and value is not None:
                metafunc.parametrize("c", value.split(","))
    
    def test_functionality(get_A_list, get_B_list, c):
        print(f"test_functionality {get_A_list}, {get_B_list}, {c}")
    

    这将创建所需的参数化测试:

    pytest -vv --A_list="A1,A2" --B_list="B1" --C_list="C1,C2":

    ================================================= test session starts =================================================
    ...
    collected 4 items
    
    so/cmd_line_args/test.py::test_functionality[C1-A1-B1] PASSED                                                    [ 25%]
    so/cmd_line_args/test.py::test_functionality[C2-A1-B1] PASSED                                                    [ 50%]
    so/cmd_line_args/test.py::test_functionality[C1-A2-B1] PASSED                                                    [ 75%]
    so/cmd_line_args/test.py::test_functionality[C2-A2-B1] PASSED                                                    [100%]
    
    ================================================== 4 passed in 0.28s ==================================================
    

    请注意,参数顺序不是我们所希望的,但这只是一个表面问题。这是print 函数的输出:

    pytest -s --A_list="A1,A2" --B_list="B1" --C_list="C1,C2"

    ================================================= test session starts =================================================
    ...
    collected 4 items
    
    test.py get_A_list: A1
    get_B_list: A1,  B1
    test_functionality A1, B1, C1
    .test_functionality A1, B1, C2
    .get_A_list: A2
    get_B_list: A2,  B1
    test_functionality A2, B1, C1
    .test_functionality A2, B1, C2
    .
    
    ================================================== 4 passed in 0.27s ==================================================
    

    【讨论】:

    • 谢谢@MrBean Bremen ......这并没有解决我的问题......我需要使用 中的元素在 fixture get_A_list 中执行一些操作A_list .... 同样,我需要使用 A_list 和 B_list 中的元素在 fixture get_B_list 中执行一些操作...... . 像这样我有 4 个夹具,最后我需要在 test_functionality 中执行测试用例,这将使用 A_list 、 B_list 、 C_list 等中的元素......我已经用最终输出以及执行顺序更新了我的问题
    • 我添加了一个更复杂的选项来实现这一点(尽管我留下了第一个答案,因为这对于大多数情况来说已经足够了)。
    • 谢谢 MrBean,我会实施你的第二个答案,看看它是否能解决我的问题。我将检查是否需要进行任何修改才能实现我的输出以及订单...这只是一个小例子,但实际上我必须在 test_functionality 中执行大约 60 个测试用例,在此之前我需要做一些操作(计划涵盖这些操作在多个夹具中)
    • 谢谢憨豆先生。除了参数顺序外,它工作正常,但它不是功能性的。正如你提到的,这是一个外观问题。根据您的解决方案,我添加了更多参数以从命令行传递。再次感谢您的解决方案
    猜你喜欢
    • 2011-02-12
    • 1970-01-01
    • 2020-12-05
    • 2017-04-14
    • 2012-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多