运行 基于命令行参数的 pytest 标记

Run pytest markers based on command line argument

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

我 运行 通过调用这些标记并行地设置不同的测试用例,就像这样 - pytest -m "markername" -n 3

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

我尝试了什么?

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

我添加了一个 conftest.py 文件,使用 pytest 添加选项添加了一个命令行选项 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",配置文件被读取,但所有测试用例都是 运行ning.

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

编辑 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
    ....

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

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

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