重命名 pytest.mark.parametrize 中的参数

Renaming parameters in pytest.mark.parametrize

我有一个代码使用目录中的文件作为参数:

def get_testcases(directory):
    files = list(os.listdir(directory))
    testcases = filter(lambda x: x.endswith('.yaml'), files)
    for testcase in testcases:
        postconf = testcase.replace('.yaml', '.conf')
        yield (
            os.path.join(directory, testcase),
            os.path.join(directory, postconf)
        )

def get_pre_configs(directory):
    for file in os.listdir(directory):
        if file.endswith('.conf'):
            yield os.path.join(directory, file)

@pytest.mark.parametrize("pre_config", get_pre_configs('pre_configs'))
@pytest.mark.parametrize("testcase_declaration, testcase_result", get_testcases('testcases'))
def test_foo(pre_config, testcase_declaration, testcase_result):
    assert testcase_declaration
    assert testcase_result
    assert pre_config

它按我的需要工作,但我不喜欢 pytest 输出:

test_interface.py::test_foo[testcases/up.yaml-testcases/up.conf-pre_configs/bad.conf] PASSED              [ 16%]
test_interface.py::test_foo[testcases/up.yaml-testcases/up.conf-pre_configs/simple.conf] PASSED           [ 33%]
test_interface.py::test_foo[testcases/up.yaml-testcases/up.conf-pre_configs/complicated.conf] PASSED      [ 50%]
test_interface.py::test_foo[testcases/down.yaml-testcases/down.conf-pre_configs/bad.conf] PASSED          [ 66%]
test_interface.py::test_foo[testcases/down.yaml-testcases/down.conf-pre_configs/simple.conf] PASSED       [ 83%]
test_interface.py::test_foo[testcases/down.yaml-testcases/down.conf-pre_configs/complicated.conf] PASSED  [100%]

有什么方法可以显示与传递给测试的值不同的测试名称?我想 trim 去掉目录名和文件名的扩展名(仅用于测试名称,我想将它们 'as is' 传递给测试)。

原来@pytest.mark.parametrize(还有@pytest.fixtures)都挺厉害的。它们允许您通过指定 ids 列表来更改每个测试的名称。诀窍是动态生成 parametrize 的参数。

我重构了您的代码(详见下文)。给定包含以下内容的本地目录:

$ find . -type f -name '*.yaml' -o -name '*.conf'
./pre_configs/yikes.conf
./pre_configs/foobar.conf
./testcases/hello.yaml
./testcases/world.yaml

那么pytest的输出是:

collecting ... collected 4 items

test_foo.py::test_foo[yikes-hello] PASSED                                [ 25%]
test_foo.py::test_foo[yikes-world] PASSED                                [ 50%]
test_foo.py::test_foo[foobar-hello] PASSED                               [ 75%]
test_foo.py::test_foo[foobar-world] PASSED                               [100%]

============================== 4 passed in 0.19s ===============================

这是重构后的代码。请注意 get_testcases()get_pre_configs() 如何都 return 一个 dict 可以用作 @pytest.mark.parametrizekwargs。特别是,ids 允许您覆盖 pytest.

使用的名称
def getfiles(directory, ext):
    """return two lists: fullpath and names (without extension)"""
    n = len(ext)
    paths, names = zip(*[
        (ent.path, ent.name[:-n])
        for ent in os.scandir(directory)
        if ent.is_file() and ent.name.endswith(ext)])
    return paths, names


def get_testcases(directory):
    ypaths, names = getfiles(directory, '.yaml')
    cpaths = [f'{os.path.splitext(s)[0]}.conf' for s in ypaths]
    return {
        'argnames': ['testcase_declaration', 'testcase_result'],
        'argvalues': zip(ypaths, cpaths),
        'ids': names}


def get_pre_configs(directory):
    paths, names = getfiles(directory, '.conf')
    return {
        'argnames': ['pre_config'],
        'argvalues': zip(paths),  # always wants a list of tuples
        'ids': names}


@pytest.mark.parametrize(**get_pre_configs('pre_configs'))
@pytest.mark.parametrize(**get_testcases('testcases'))
def test_foo(pre_config, testcase_declaration, testcase_result):
    assert os.path.isfile(pre_config)
    assert os.path.isfile(testcase_declaration)
    assert testcase_result