'pytest using fixtures as arguments in parametrize

I would like to use fixtures as arguments of pytest.mark.parametrize or something that would have the same results.

For example:

import pytest
import my_package

@pytest.fixture
def dir1_fixture():
    return '/dir1'

@pytest.fixture
def dir2_fixture():
    return '/dir2'

@pytest.parametrize('dirname, expected', [(dir1_fixture, 'expected1'), (dir2_fixture, 'expected2')]
def test_directory_command(dirname, expected):
    result = my_package.directory_command(dirname)
    assert result == expected

The problem with fixture params is that every param of the fixture will get run every time it's used, but I don't want that. I want to be able to choose which fixtures will get used depending on the test.



Solution 1:[1]

If you're on pytest 3.0 or later, I think you should be able to solve this particular scenario by writing a fixture along the lines of:

@pytest.fixture(params=['dir1_fixture', 'dir2_fixture'])
def dirname(request):
    return request.getfixturevalue(request.param)

Docs here: http://doc.pytest.org/en/latest/builtin.html#_pytest.fixtures.FixtureRequest.getfixturevalue

However, you can't use this approach if the fixture you're attempting to dynamically load is parametrized.

Alternatively, you might be able to figure something out with the pytest_generate_tests hook. I haven't been able to bring myself to look into that much, though.

Solution 2:[2]

Will was on the right path, you should use request.getfixturevalue to retrieve the fixture.

But you can do it right in the test, which is simpler.

@pytest.mark.parametrize('dirname, expected', [
    ('dir1_fixture', 'expected1'),
    ('dir2_fixture', 'expected2')])
def test_directory_command(dirname, expected, request):
    result = my_package.directory_command(request.getfixturevalue(dirname))
    assert result == expected

Another way is to use lazy-fixture plugin:

@pytest.mark.parametrize('dirname, expected', [
    (pytest.lazy_fixture('dir1_fixture'), 'expected1'),
    (pytest.lazy_fixture('dir2_fixture'), 'expected2')])
def test_directory_command(dirname, expected):
    result = my_package.directory_command(dirname)
    assert result == expected

Solution 3:[3]

As for now, my only solution is to create a fixture that returns a dictionary of fixtures.

import pytest
import my_package

@pytest.fixture
def dir1_fixture():
    return '/dir1'

@pytest.fixture
def dir2_fixture():
    return '/dir2'

@pytest.fixture
def dir_fixtures(
    dir1_fixture,
    dir2_fixture
    ):
    return {
        'dir1_fixture': dir1_fixture,
        'dir2_fixture': dir2_fixture
    }

@pytest.mark.parametrize('fixture_name, expected', [('dir1_fixture', 'expected1'), ('dir2_fixture', 'expected2')]
def test_directory_command(dir_fixtures, fixture_name, expected):
    dirname = dir_fixtures[fixture_name]
    result = my_package.directory_command(dirname)
    assert result == expected

Not the best since it does not use a solution built into pytest, but it works for me.

Solution 4:[4]

This isn't currently supported by pytest. There is an open feature request for it though: https://github.com/pytest-dev/pytest/issues/349.

Solution 5:[5]

DO NOT TRY TO CHANGE FIXTURE PARAMETERS DURING TEST EXECUTION

Invalid example: @pytest.fixture(scope="class", params=other_fixture)

Now I'll explain why it doesn't work:

  1. Pytest creates session objects before running the test, containing the parameters with which the test will run. During the execution of the test; you cannot change the parameters

  2. If you really want to do this (change the parameters dynamically), you can use an intermediate text file: "params.txt". Example: @pytest.fixture(scope="class", params=json.load(open("topics.txt"))). Again, you will not be able to change the content of the file during the test; because if you change it; will not be visible in the test. To do this; we need to change the contents of the file when the program starts and before the session objects are created. To do that; define a method pytest_sessionstart(session) in conftest.py where you change the file content.

  3. For more details; check this documentation: How to run a method before all tests in all classes? and https://docs.pytest.org/en/6.2.x/reference.html#pytest.hookspec.pytest_sessionstart

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Will
Solution 2 Gino Mempin
Solution 3 elveatles
Solution 4 Robie Basak
Solution 5 Dorcioman