pytest icon indicating copy to clipboard operation
pytest copied to clipboard

Using fixtures in pytest.mark.parametrize

Open pytestbot opened this issue 11 years ago • 116 comments

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

pytestbot avatar Aug 30 '13 19:08 pytestbot

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.

pytestbot avatar Jun 21 '14 11:06 pytestbot

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.

pytestbot avatar Jun 25 '14 09:06 pytestbot

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

pytestbot avatar Aug 03 '14 01:08 pytestbot

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.

pytestbot avatar Sep 09 '14 15:09 pytestbot

Here's a related stackoverflow question: http://stackoverflow.com/questions/24340681/how-to-concatenate-several-parametrized-fixtures-into-a-new-fixture-in-py-test

mgeier avatar Jun 16 '15 09:06 mgeier

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.

jlmenut avatar Nov 19 '15 09:11 jlmenut

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

kevincox avatar Feb 19 '16 15:02 kevincox

+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

SUNx2YCH avatar Feb 26 '16 16:02 SUNx2YCH

+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 avatar Jul 04 '16 20:07 rabbbit

@rabbbit your example is structurally wrong and runs fixture code at test importation time

RonnyPfannschmidt avatar Jul 05 '16 06:07 RonnyPfannschmidt

@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?

rabbbit avatar Jul 05 '16 09:07 rabbbit

There is a upcoming proposal wrt "merged" fixtures, there is no implementation yet

RonnyPfannschmidt avatar Jul 05 '16 12:07 RonnyPfannschmidt

For reference: #1660

nicoddemus avatar Jul 05 '16 12:07 nicoddemus

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?

rabbbit avatar Jul 05 '16 14:07 rabbbit

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

RonnyPfannschmidt avatar Jul 05 '16 14:07 RonnyPfannschmidt

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 .

rabbbit avatar Jul 05 '16 14:07 rabbbit

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

RonnyPfannschmidt avatar Jul 05 '16 15:07 RonnyPfannschmidt

It would be really convenient to have this functionality, perhaps along the lines of "LazyFixture" in pytest-factoryboy

kibernick avatar Sep 27 '16 09:09 kibernick

@kibernick we already did put a outline of possible implementations into the documentation

we just need time or a person implementing it

RonnyPfannschmidt avatar Sep 27 '16 09:09 RonnyPfannschmidt

@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

Brachi avatar Sep 30 '16 05:09 Brachi

@RonnyPfannschmidt, can you please check this out https://github.com/TvoroG/pytest-fixture-mark? Need some feedback

TvoroG avatar Oct 02 '16 21:10 TvoroG

@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 avatar Oct 03 '16 17:10 Brachi

@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.

TvoroG avatar Oct 04 '16 07:10 TvoroG

@Brachi, I fixed it. Let me know if there is more such cases when plugin is failing

TvoroG avatar Oct 04 '16 19:10 TvoroG

@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)

Brachi avatar Oct 04 '16 21:10 Brachi

@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? 😉

nicoddemus avatar Oct 04 '16 21:10 nicoddemus

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 .

rabbbit avatar Oct 04 '16 21:10 rabbbit

I like how it played out, but @hackebrot and @hpk42 had something else in mind during the sprint. 😁 Perhaps they can comment on this.

nicoddemus avatar Oct 04 '16 21:10 nicoddemus

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.

Brachi avatar Oct 04 '16 22:10 Brachi

i think its critical to make the fixture request NOT be a mark object as well

RonnyPfannschmidt avatar Oct 05 '16 06:10 RonnyPfannschmidt