在参数化中使用 fixture 作为参数的 pytest

我希望使用 fixture 作为 pytest.mark.parametrize的参数或者具有相同结果的参数。

例如:

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

Fixture 参数的问题在于,每次使用 fixture 的每个参数时,都会运行它,但我不希望出现这种情况。我希望能够选择哪些夹具将得到使用取决于测试。

43431 次浏览

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.

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

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

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.

This isn't currently supported by pytest. There is an open feature request for it though (which has been opened in 2013).

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

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