Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using fixture return value as value in mark.parametrize()

Tags:

python

pytest

my question is - is it possible to use return value from fixture as a value in parametrize? 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? 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.

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?

like image 960
Taku Avatar asked Feb 24 '26 19:02

Taku


1 Answers

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.

But I have an alternative way to achieve similar result, mainly by extending pytest plugin pytest_generate_tests and using method metafunc.parametrize. https://pytest.org/latest/parametrize.html#basic-pytest-generate-tests-example

Here is my solution. In 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. 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)
like image 97
Li Feng Avatar answered Feb 27 '26 08:02

Li Feng



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!