First of all, sorry for the title, I couldn't think of something better.
The code below is fictitious because the real code where this is happening is much longer and this little example shows the same problem.
I have this code in my conftest.py
:
@pytest.fixture()
def my_fixture(argument):
return f'expected{argument[-1]}'
It works as expected when parametrizing only the fixture in a test.
But if I have this test function:
@pytest.mark.parametrize('my_fixture, expected', [
('fixture_param1', 'expected1'),
('fixture_param2', 'expected2')
], indirect=['my_fixture'])
def test_indirect(my_fixture, expected):
value = my_fixture
assert value == expected
This fails with the following error:
@pytest.fixture()
def my_fixture(argument):
E fixture 'argument' not found
> available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, cov, doctest_namespace, log_paths, monkeypatch, my_fixture, no_cover, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, unreadable_file
> use 'pytest --fixtures [testpath]' for help on them.
Obviously this works if the test function is called like this:
@pytest.mark.parametrize('argument, expected', [
('fixture_param1', 'expected1'),
('fixture_param2', 'expected2')
], indirect=['my_fixture'])
def test_indirect(my_fixture, expected):
value = my_fixture
assert value == expected
but I find this syntax confusing because I would like to have my_fixture
explicitly in the parametrization rather than the argument name.
What am I doing wrong? Apparently, what I try to do is correct, one can use both a fixture and a normal parameter in a parametrize
call as long as the indirect parametrizations are declared. I'm at a loss here…
Thanks in advance!
Well, turns out the solution is quite simple, and as far as I know, quite undocumented. I mean, the mechanism is perfectly documented, but the fact that is the only way of achieving what I needed is not.
Using this:
# Before it was 'argument' instead of 'request.param'
@pytest.fixture()
def my_fixture(request):
return f'expected{request.param[-1]}'
makes everything work using the code which failed for me at first:
# Now, it works!
@pytest.mark.parametrize('my_fixture, expected', [
('fixture_param1', 'expected1'),
('fixture_param2', 'expected2')
], indirect=['my_fixture'])
def test_indirect(my_fixture, expected):
value = my_fixture
assert value == expected
Turns out that using request.param
solved the problem because the original code made pytest
think that argument
was a fixture (don't ask me why), and of course request
IS a fixture, so problem solved. Weren't for the fact that request
fixture allows accesing parametrized params, this would not work.
If I find documentation related to this (I found the solution by sheer accident after endless hours investigating on the net) I'll post here in this reply for completeness.
Sorry for the noise!