简体   繁体   English

使用fixture返回值作为mark.parametrize()中的值

[英]using fixture return value as value in mark.parametrize()

my question is - is it possible to use return value from fixture as a value in parametrize? 我的问题是 - 是否可以使用fixture中的返回值作为参数化中的值? The problem is - I'd like to dynamically get possible values (for example, available systems on a virtual server) for parametrize. 问题是 - 我想动态获取参数化的可能值(例如,虚拟服务器上的可用系统)。 I can access these when a virtual server is created by one of the fixtures. 当其中一个灯具创建虚拟服务器时,我可以访问这些。 Tests look like this (pseudo-code-ish): 测试看起来像这样(伪代码):

[conftest.py]

@pytest_fixture(scope='session')
def test_server(request):
    test_server = Server([default_params])
    test_server.add()
    def fin():
        test_server.delete()
    request_addfinalizer(fin)
    return test_server()

[tests.py]

def test_basic_server(test_server):
    systems = test.server.get_available_systems()
    for system in systems:
        test_server.install(system)
        test_server.run_checks()
        test_server.uninstall(system)

def test_other(test_server):
    [other tests]

etc

This way, one server is added for each session, then all tests run on it, and after session ends, server is removed. 这样,为每个会话添加一个服务器,然后在其上运行所有测试,并在会话结束后删除服务器。 But is there a way to get the available systems in @pytest.mark.parametrize without explicitly listing them (statically as a list in parametrize), using the method from server that is added when the session begins? 但有没有办法在@ pytest.mark.parametrize中获取可用的系统而不显式列出它们(静态地作为参数化中的列表),使用会话开始时添加的服务器中的方法? That way each system would run in a separate test. 这样每个系统都可以在单独的测试中运行。

I tried using test_server in another fixture and then returning the list (the same way test_server is returned by test_server fixture, but I cannot use that as a value in parametrize - since decorator is evaluated before the test_server fixture is called in any test, and getting the list depends on test_server fixture. 我尝试在另一个fixture中使用test_server然后返回列表(test_server fixture返回test_server的方式相同,但是我不能将它用作参数化中的值 - 因为在任何测试中调用test_server fixture之前评估decorator,并获得该列表取决于test_server fixture。

This would be ideal: 这将是理想的:

[tests.py]

@pytest.mark.parametrize('system',[systems_list <- dynamically generated
                                             when the server is created])

def test_basic_server(test_server,system):
    test_server.install(system)
    test_server.run_checks()
    test_server.uninstall(system)

This is just a very basic example, in my tests I need to parametrize based on multiple scenarios and values and I end up with giant arrays when I do it statically. 这只是一个非常基本的例子,在我的测试中,我需要根据多个场景和值进行参数化,当我静态执行时,我最终会得到巨型数组。

But the principle remains the same - basically: can I call the fixture before the first test using this fixture runs, or how can pytest.mark.parametrize() access fixture values? 但原理保持不变 - 基本上:我可以在使用此夹具运行的第一次测试之前调用夹具,或者pytest.mark.parametrize()如何访问夹具值?

I think you may be unable to achieve what you want directly. 我想你可能无法直接达到你想要的效果。 Because @pytest.mark.parametrize is being called during collecting, and fixtures will be called after collection completed. 因为在收集过程中会调用@pytest.mark.parametrize ,并且在收集完成后将调用fixture。

But I have an alternative way to achieve similar result, mainly by extending pytest plugin pytest_generate_tests and using method metafunc.parametrize . 但是我有一个替代的方式来达到同样的效果,主要是通过扩展插件pytest pytest_generate_tests和使用方法metafunc.parametrize https://pytest.org/latest/parametrize.html#basic-pytest-generate-tests-example https://pytest.org/latest/parametrize.html#basic-pytest-generate-tests-example

Here is my solution. 这是我的解决方案。 In conftest.py 在conftest.py中

class System(object):
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return "<System '{}'>".format(self.name)

def get_available_systems():
    return [System('A'), System('B'), System('C')]


def pytest_generate_tests(metafunc):
    if 'system' in metafunc.fixturenames:
        available_systems = get_available_systems()

        metafunc.parametrize('system', available_systems)

In test file: 在测试文件中:

def test_basic_server(system):
    print(system)

This is the output, you will have access to each system in test. 这是输出,您将可以访问测试中的每个系统。

collected 3 items

test_01.py::test_basic_server[system0] <System 'A'>
PASSED
test_01.py::test_basic_server[system1] <System 'B'>
PASSED
test_01.py::test_basic_server[system2] <System 'C'>
PASSED

The bad thing is, get_available_systems will be called every time whenever using fixture system , which is not what you want. 不好的是,每次使用夹具system时都会调用get_available_systems ,这不是你想要的。 But I think it's not hard to add some extra logic to make the query logic only be executed once. 但我认为添加一些额外的逻辑以使查询逻辑只执行一次并不困难。

For example: 例如:

def pytest_generate_tests(metafunc):
    if 'system' in metafunc.fixturenames:
        if hasattr(metafunc.config, 'available_systems'):
            available_systems = metafunc.config.available_systems
        else:
            available_systems = get_available_systems()
            metafunc.config.available_systems = available_systems
        metafunc.parametrize('system', available_systems)

I was able to work a similar problem where I have to generate tests data that is to be used for parameterization on the fly: 我能够处理类似的问题,我必须生成用于即时参数化的测试数据:

class TestFilters(object):
 cls_testdata1 = []

def setup_class(cls):
 r = []
 for i in range(5):
   r.append((x, y, z))
 TestFilters.cls_testdata1 = r

@pytest.mark.parametrize("filter_id", list(range(5)))
def test_func(self, filter_id):
    params = TestFilters.cls_testdata1[filter_id]

... ...

This would support adding the parameters dynamically, only that you need to predetermine the number of tests. 这将支持动态添加参数,只需要预先确定测试次数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM