首页 > 解决方案 > 根据命令行参数运行 pytest 标记

问题描述

我有一个 python 文件,它从配置文件中读取并初始化某些变量,然后是一些由 pytest 标记定义的测试用例。

我通过调用这些标记并行运行不同的测试用例集,如下所示 -pytest -m "markername" -n 3

现在的问题是,我没有一个配置文件了。有多个配置文件,我需要在执行期间从命令行获取用于测试用例的配置文件的方法。

我试过什么?

我将配置文件的读取包装到一个带有 conf 参数的函数中。

我添加了一个文件,使用 pytest addoptionconftest.py添加了一个命令行选项。conf

def pytest_addoption(parser):
    parser.addoption("--conf", action="append", default=[],
        help="Name of the configuration file to pass to test functions")

def pytest_generate_tests(metafunc):
    if 'conf' in metafunc.fixturenames:
        metafunc.parametrize("conf", metafunc.config.option.conf)
        

然后尝试了这个pytest -q --conf="configABC" -m "markername",希望我可以读取该配置文件以初始化某些参数并将其传递给包含给定标记的测试用例。但是什么都没有发生,我想知道...我想知道如何...我想知道为什么...

如果我运行pytest -q --conf="configABC",则读取配置文件,但所有测试用例都在运行。

但是,我只需要运行使用通过我从命令行获得的配置文件初始化的变量的特定测试用例。我想使用标记,因为我也在使用参数化并并行运行它们。如何从命令行获取要使用的配置文件?我把这搞砸了吗?

编辑1:

#contents of testcases.py

import json
import pytest

...
...
...

def getconfig(conf):
    config = open(str(conf)+'_Configuration.json', 'r')
    data = config.read()
    data_obj = json.loads(data)
    globals()['ID'] = data_obj['Id']
    globals()['Codes'] = data_obj['Codes']          # list [Code_1, Code_2, Code_3]
    globals()['Uname'] = data_obj['IM_User']
    globals()['Pwd'] = data_obj['IM_Password']
    #return ID, Codes, User, Pwd

def test_parms():
    #Returns a list of tuples [(ID, Code_1, Uname, Pwd), (ID, Code_2, Uname, Pwd), (ID, Code_3, Uname, Pwd)]
    ...
    ...
    return l

@pytest.mark.testA
@pytest.mark.parametrize("ID, Code, Uname, Pwd", test_parms())
def testA(ID, Code, Uname, Pwd):
    ....
    do something
    ....

@pytest.mark.testB
@pytest.mark.parametrize("ID, Code, Uname, Pwd", test_parms())
def testB(ID, Code, Uname, Pwd):
    ....
    do something else
    ....

标签: pythonpytestfixturespytest-markers

解决方案


您似乎走在正确的轨道上,但错过了一些联系和细节。

首先,您的选项看起来有点奇怪 - 据我了解,您只需要一个字符串而不是列表:

conftest.py

def pytest_addoption(parser):
    parser.addoption("--conf", action="store",
                     help="Name of the configuration file"
                          " to pass to test functions")

在您的测试代码中,您读取配置文件,并根据您的代码,它包含参数列表的 json 字典,例如:

{
  "Id": [1, 2, 3],
  "Codes": ["a", "b", "c"],
  "IM_User": ["User1", "User2", "User3"],
  "IM_Password": ["Pwd1", "Pwd2", "Pwd3"]
}

参数化需要的是参数元组列表,并且您还希望只读取一次列表。这是一个示例实现,它在首次访问时读取列表并将其存储在字典中(假设您的配置文件如上所示):

import json

configs = {}

def getconfig(conf):
    if conf not in configs:
        # read the configuration if not read yet
        with open(conf + '_Configuration.json') as f:
            data_obj = json.load(f)
        ids = data_obj['Id']
        codes = data_obj['Codes']
        users = data_obj['IM_User']
        passwords = data_obj['IM_Password']
        # assume that all lists have the same length
        config = list(zip(ids, codes, users, passwords))
        configs[conf] = config
    return configs[conf] 

现在您可以使用这些参数来参数化您的测试:

def pytest_generate_tests(metafunc):
    conf = metafunc.config.getoption("--conf")
    # only parametrize tests with the correct parameters
    if conf and metafunc.fixturenames == ["uid", "code", "name", "pwd"]:
        metafunc.parametrize("uid, code, name, pwd", getconfig(conf))

@pytest.mark.testA
def test_a(uid, code, name, pwd):
    print(uid, code, name, pwd)


@pytest.mark.testB
def test_b(uid, code, name, pwd):
    print(uid, code, name, pwd)

def test_c():
    pass

在此示例中,两者test_atest_b都将被参数化,但不是test_c

如果您现在运行测试(使用 json 文件名“ConfigA_Configuration.json”),您会得到如下内容:

$ python -m pytest -v --conf=ConfigA -m testB testcases.py

============================================ 6 passed, 2 warnings in 0.11s ============================================

(Py37_new) c:\dev\so\questions\so\params_from_config>python -m pytest -v --conf=ConfigA -m testB test_params_from_config.py

...
collected 7 items / 4 deselected / 3 selected

test_params_from_config.py::test_b[1-a-User1-Pwd1] PASSED
test_params_from_config.py::test_b[2-b-User2-Pwd2] PASSED
test_params_from_config.py::test_b[3-c-User3-Pwd3] PASSED

推荐阅读