pytest
pytest copied to clipboard
Using fixtures in pytest.mark.parametrize
Originally reported by: Florian Rathgeber (BitBucket: frathgeber, GitHub: frathgeber)
I often have a use case like the following contrived example:
@pytest.fixture
def a():
return 'a'
@pytest.fixture
def b():
return 'b'
@pytest.mark.parametrize('arg', [a, b])
def test_foo(arg):
assert len(arg) == 1
This doesn't currently do what's intended i.e. arg
inside the test function is not the fixture value but the fixture function.
I can work around it by introducing a "meta fixture", but that's rather ugly:
@pytest.fixture(params=['a', 'b'])
def arg(request, a, b):
return {'a': a, 'b': b}[request.param]
def test_foo(arg):
assert len(arg) == 1
It would be convenient if a syntax like in the first case was supported.
- Bitbucket: https://bitbucket.org/pytest-dev/pytest/issue/349
Original comment by Matthias Geier (BitBucket: geier, GitHub: geier):
This would be a great feature!
I found another (I don't know if more or less ugly) work-around:
#!python
@pytest.mark.parametrize('arg', ['a', 'b'])
def test_foo(arg, request):
val = request.getfuncargvalue(arg)
assert len(val) == 1
This doesn't work, however, with parametrized fixtures.
BTW, it would also be great if fixtures were supported in the params
argument of pytest.fixture
.
Original comment by Floris Bruynooghe (BitBucket: flub, GitHub: flub):
Tentatively assigning this to me as I think I might be able to come up with a reasonable patch. It'll probably take me a long while though so don't let that discourage anyone else from working on this, assigning it more as a way of not forgetting about it.
Original comment by Praveen Shirali (BitBucket: praveenshirali, GitHub: praveenshirali):
The quoted examples work because functions a
and b
are part of the same module as test_foo
, and within the scope of the example, the parametrization should work even if @pytest.fixture
decorator isn't present around functions a
and b
. They are getting used as regular python functions and not as pytest fixtures. Note that fixtures can also be defined in external modules like conftest.py
.
Another alternative to the above example is to directly call these functions in the list.
#!python
@pytest.mark.parametrize('arg', [a(), b()])
def test_foo(arg):
assert len(arg) == 1
Original comment by BitBucket: dpwrussell, GitHub: dpwrussell:
This would be an awesome feature.
@praveenshirali I don't think your alternative is used as a fixture, it just calls the fixture function. So it would be run repeatedly. You would also have to specify the arguments to the fixture if there were any which could begin the cycle over again if they are also fixtures.
Here's a related stackoverflow question: http://stackoverflow.com/questions/24340681/how-to-concatenate-several-parametrized-fixtures-into-a-new-fixture-in-py-test
Yes, I would like very much to have this feature also. Maybe a line in the doc explaining it's not possible for the moment would be useful also.
It would also be killer if this supported parameterized fixtures generating the product of the fixtures. Although this might be a little much.
@pytest.fixture(params=["1", " ", 1, True, [None], {1:2}])
def truthy(request):
return request.param
@pytest.fixture(params=[False, None, "", 0, [], {}])
def falsey(request):
return request.param
@pytest.mark.parameterize("val,res", [
(truthy, True),
(falsey, False),
])
def test_bool(val, res)
assert bool(val) is res
+1 for this feature. BTW, combining Florian Rathgeber and Matthias Geier solutions we can get a bit nicer "meta fixture":
@pytest.fixture
def a():
return 'a'
@pytest.fixture
def b():
return 'b'
@pytest.fixture(params=['a', 'b'])
def arg(request):
return request.getfuncargvalue(request.param)
def test_foo(arg):
assert len(arg) == 1
+1 on this.
I'm currently writing tests that look like:
@pytest.fixture
def my_fixture():
return 'something'
@pytest.fixture
def another_fixture(my_fixture):
return {'my_key': my_fixture}
def yet_another_fixture():
return {'my_key': None}
@pytest.mark.parametrize('arg1, arg2', [
(5, another_fixture(my_fixture())),
(5, yet_another_fixture()),
)
def my_test(arg1, arg2):
assert function_under_test(arg2) == arg1
and that's rather ugly.
@rabbbit your example is structurally wrong and runs fixture code at test importation time
@RonnyPfannschmidt I know - and that's why I'd like to be able to use fixtures in parametrize
? And that would be awesome.
My example is wrong, but it follows the guideline of "always use fixtures". Otherwise we'd end up with fixtures in normal tests, and workarounds in parametrized tests.
Or is there a way of achieving this already, outside of dropping parametrize and doing 'if/elses' in the test function?
There is a upcoming proposal wrt "merged" fixtures, there is no implementation yet
For reference: #1660
ok, I don't understand python.
If the below works:
@pytest.fixture
def my_fixture
return 1
def test_me(my_fixture):
assert 1 == my_fixture
wouldn't the below be simpler? And an exact equivalent?
@pytest.fixture
def my_fixture
return 1
@pytest.mark.parametrize('fixture', [my_fixture])
def test_me(fixture):
assert 1 == my_fixture
Am I wrong to think that mark.parametrize
could figure out whether an argument is a pytest.fixture
or not?
atm parametrize cannot figure it, and it shouldnt figure it there will be a new object to declare a parameter will request a fixture/fixture with parameters
some documentation for that is in the features branch
yeah, I read the proposal.
I'm just surprised you're going with pytest.fixture_request(' default_context')
it feels very verbose?
after all,
@pytest.fixture
def my_fixture
return 1
def test_me(my_fixture):
assert 1 == my_fixture
could also turn to
@pytest.fixture
def my_fixture
return 1
def test_me(pytest.fixture_request(my_fixture)):
assert 1 == my_fixture
but that's not the plan, right?
On 5 July 2016 at 16:17, Ronny Pfannschmidt [email protected] wrote:
atm parametrize cannot figure it, and it shouldnt figure it there will be a new object to declare a parameter will request a fixture/fixture with parameters
some documentation for that is in the features branch
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/pytest-dev/pytest/issues/349#issuecomment-230490966, or mute the thread https://github.com/notifications/unsubscribe/AARt0kUxVSL1GMeCtU-Kzy3Pg80mW7Ouks5qSmdmgaJpZM4FEMDj .
thats not even valid python syntax
the fixture-request would be used as a parameter value to tell py.test "use the value of a fixture you create later on"
there are already parametzw using examples, and its not overly verbose
It would be really convenient to have this functionality, perhaps along the lines of "LazyFixture" in pytest-factoryboy
@kibernick we already did put a outline of possible implementations into the documentation
we just need time or a person implementing it
@RonnyPfannschmidt can you link to that part of the documentation you mention? Can't find it. Edit: nevermind. http://doc.pytest.org/en/latest/proposals/parametrize_with_fixtures.html
@RonnyPfannschmidt, can you please check this out https://github.com/TvoroG/pytest-fixture-mark? Need some feedback
@TvoroG good work. Currently this seems to be failing. Is it possible to support it as well?
import pytest
@pytest.fixture(params=[1, 2, 3])
def one(request):
return str(request.param)
@pytest.fixture
def two():
return 4
@pytest.fixture(params=[
pytest.mark.fixture('one'),
pytest.mark.fixture('two')
])
def some(request):
return request.param
def test_func(some):
assert some in {'1', '2', '3', 4}
@Brachi, thanks for catching it! It works now, but more nested structures need some dependency sorting to instantiate fixtures in correct order. I'll update the plugin code when i'm done.
@Brachi, I fixed it. Let me know if there is more such cases when plugin is failing
@TvoroG great, thanks for the quick reply. I tested it a little more and here's another contrived example that doesn't work, based on a real use case (where one
actually returns an object)
import pytest
@pytest.fixture(params=[1, 2, 3])
def one(request):
return request.param
@pytest.fixture(params=[pytest.mark.fixture('one')])
def as_str(request):
return str(request.getfixturevalue('one'))
@pytest.fixture(params=[pytest.mark.fixture('one')])
def as_hex(request):
return hex(request.getfixturevalue('one'))
def test_as_str(as_str):
assert as_str in {'1', '2', '3'}
def test_as_hex(as_hex):
assert as_hex in {'0x1', '0x2', '0x3'}
# fails at setup time, with ValueError: duplicate 'one'
def test_as_hex_vs_as_str(as_str, as_hex):
assert int(as_hex, 16) == int(as_str)
@TvoroG, pytest-fixture-mark
seems very nice! I hope I get the chance to try it soon! 😄
Perhaps discussion specific to it should be moved to https://github.com/TvoroG/pytest-fixture-mark thought? 😉
Hoping that this could get merged into pytest would be too much, right? :)
On 4 Oct 2016 23:24, "Bruno Oliveira" [email protected] wrote:
@TvoroG https://github.com/TvoroG, pytest-fixture-mark seems very nice! I hope I get the chance to try it soon! 😄
Perhaps discussion specific to it should be moved to https://github.com/TvoroG/pytest-fixture-mark thought? 😉
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/pytest-dev/pytest/issues/349#issuecomment-251518117, or mute the thread https://github.com/notifications/unsubscribe-auth/AARt0vvrzeWro3wD9-G1nKn1H89OrLCJks5qwsP8gaJpZM4FEMDj .
I like how it played out, but @hackebrot and @hpk42 had something else in mind during the sprint. 😁 Perhaps they can comment on this.
Perhaps discussion specific to it should be moved to
I was thinking the same thing before posting, but probably the use cases and limitations are useful as well for other implementations.
i think its critical to make the fixture request NOT be a mark object as well