Reputation: 2730
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.
Upvotes: 184
Views: 107116
Reputation: 1068
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.
Upvotes: 65
Reputation: 1
An addition to the accepted answer for async tests:
lazy_fixture
also work on async tests, where as the getfixturevalue
-method doesn't, as documented in an open issue in pytest-asyncio:
@pytest.fixture
async def a():
return 1
@pytest.fixture
async def b():
return 2
@pytest.mark.asyncio
@pytest.mark.parametrize('i', [pytest.lazy_fixture('a'), pytest.lazy_fixture('b')])
async def test_this(i):
assert i in [1, 2]
Upvotes: -1
Reputation: 1
For example, with request.getfixturevalue(), you can call fruits()
and meat()
fixtures by the names set in @pytest.mark.parametrize() as shown below:
import pytest
@pytest.fixture
def fruits():
return "orange"
@pytest.fixture
def meat():
return "beef"
@pytest.mark.parametrize(
("food", "expected"),
(
("fruits", "orange"),
("meat", "beef")
)
)
def test(request, food, expected):
result = request.getfixturevalue(food) # Here
print(result, expected)
assert result == expected
Then, request.getfixturevalue()
could call fruits()
and meat()
fixtures by the names set in @pytest.mark.parametrize()
as shown below:
$ pytest -q -rP
.. [100%]
=============== PASSES ================
_________ test[fruits-orange] _________
-------- Captured stdout call ---------
orange orange
___________ test[meat-beef] ___________
-------- Captured stdout call ---------
beef beef
2 passed in 0.10s
Upvotes: 0
Reputation: 6979
This might be off topic but I came here trying to figure out how to read a whole dir. of test data files and run them, one by one as a fixture. So if you had all the dirs in a folder say test/examples
you could do something like:
import pytest
from pathlib import Path
@pytest.fixture(
# I guess this is anything that generates a list
# Make it a list of tuples if you want results as well, or JSON?
params=[str(p) for p in Path("test/examples").glob("*") if p.is_dir()]
)
def dirname(request):
return request.param
def test_directory_command(dirname): # dirname will be enumerated
result = my_package.directory_command(dirname)
assert ...
Check the docs
Upvotes: 0
Reputation: 51
You can use one parametrised fixture instead of 2 different and then use indirect
.
import pytest
import my_package
@pytest.fixture
def dirname(request):
return request.param
@pytest.mark.parametrize(
'dirname, expected',
[('/dir1', 'expected1'), ('/dir2', 'expected2')],
indirect=['dirname']
)
def test_directory_command(dirname, expected):
result = my_package.directory_command(dirname)
assert result == expected
In this case you parametrise you fixture using request (thanks to indirect parameter), and expected
is just a simple param
Upvotes: 5
Reputation: 6750
This isn't currently supported by pytest. There is an open feature request for it though (which has been opened in 2013).
Upvotes: 16
Reputation: 2730
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.
Upvotes: 16
Reputation: 636
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:
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
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.
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
Upvotes: -1
Reputation: 2682
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
Upvotes: 122