如何在pytest中对特定参数运行特定测试

问题描述 投票:0回答:1

我正在为一组略有不同的设备开发一个自动化测试套件。我正在使用 pytest 框架。测试驱动设备,并检查其响应如何。

该设备由多个相同的通道组成,因此我想利用 pytest 参数化功能,并为每个通道运行测试。与设备通道的通信非常复杂,因此我将其隐藏在通道装置中(在下面的代码中,它仅返回通道名称,但实际上它创建了一个复杂的对象)。我没有使用 pytest.mark.parametrize,而是制作了一个参数化夹具,为每个通道创建并运行一个对象。

channels = ["left", "right"]

@pytest.fixture(scope = 'function', params = channels)
def channel(request):
    id = request.param[0]   # TODO: Create channel object here, return ID for now
    yield id

def test_channel(channel):
    print(channel)
    pass

到目前为止一切顺利。但现在我需要根据设置更改频道列表 - 不同的设备可能会公开不同数量的频道及其名称。我需要类似的东西

@pytest.fixture(scope="session")
def channels_list(pytestconfig):
    target_device = pytestconfig.getini('target_device')

    if target_device == "DeviceA":
        return ["left", "right"]
    if target_device == "DeviceB":
        return ["up", "center", "down"]

我将其作为固定装置,以便利用 pytestconfig 基础设施能够在 INI 文件中定义目标设备,或通过命令行覆盖它。

但问题是:如何使用channels_list灯具来参数化其他灯具(通道)?它说我不能直接使用固定功能。

pytest
1个回答
0
投票

我终于弄清楚如何根据配置文件或命令行参数进行参数化测试。我还做了更具挑战性的事情:参数化列表由对组成 - 测试中使用的 ID,以及测试参数中显示的人类可读名称。

conftest.py:

def pytest_addoption(parser):
    parser.addini('target_device',       'Name of the target board (options: DeviceA, DeviceB)')


def channels_list(target_device):
    if target_device == "DeviceA":
        return [(1, "left"), (2, "right")]
    if target_device == "DeviceB":
        return [(3, "up"), (4, "center"), (5, "down")]


def pytest_generate_tests(metafunc):
    if "channel_descr" in metafunc.fixturenames:
        target_device = metafunc.config.getini("target_device")
        metafunc.parametrize("channel_descr", channels_list(target_device), ids=lambda x: x[1])

因此,使用“channel_descr”的所有内容都将使用列表之一进行参数化,具体取决于所选的目标板。

但还有更多。您可以在创建参数列表和参数化测试之间使用固定装置:

class Channel:
    def __init__(self, descr):
        print(f"Create a new channel object: {descr[1]}")
        self.id = descr[0]
        self.name = descr[1]
    

@pytest.fixture(scope = 'function')
def channel(channel_descr):
    return Channel(channel_descr)

最后简单使用参数化夹具进行测试:

def test_any(channel):
    print(channel.id)

将对所选设备中的所有通道执行此测试。此外,测试代码使用通道 ID,而通道名称用作参数的人类可读名称。

test_test.py::test_any[left] PASSED
test_test.py::test_any[right] PASSED

元函数还可以用于根据运行时条件禁用某些测试。

conftest.py:

def is_good_device(target_device):
    return target_device in ["DeviceA"]


def pytest_collection_modifyitems(config, items):
    target_device = config.getini("target_device")
    for item in items:
        if "good_device" in item.keywords and not is_good_device(target_device):
            item.add_marker(pytest.mark.skip(reason="Not a good device"))

测试:

@pytest.mark.good_device
def test_good_device(channel):
    print(channel.id)

此方法与标准方法的区别在于,条件是在运行时计算的,并且“is_good_device”函数可能非常重要。

test_test.py::test_any[up] PASSED                                                                                                                      [ 16%] 
test_test.py::test_any[center] PASSED                                                                                                                  [ 33%]
test_test.py::test_any[down] PASSED                                                                                                                    [ 50%]
test_test.py::test_good_device[up] SKIPPED (Not a good device)                                                                                         [ 66%]
test_test.py::test_good_device[center] SKIPPED (Not a good device)                                                                                     [ 83%]
test_test.py::test_good_device[down] SKIPPED (Not a good device)                                                                                       [100%]  
© www.soinside.com 2019 - 2024. All rights reserved.