[TIP] pytest - parametrizing some arguments require parametrization at collection phase and some at setup time?

Elizabeth Lin elin at splunk.com
Fri Apr 5 14:29:32 PDT 2013


Thanks Holger - that works!

We've been using pytest since the 0.9.2 days, which is why we have a lot
of old tests which haven't yet been updated to use the later pytest
features.  Switching everything over to us @pytest.mark.parametrize as
well as fixtures instead of funcargs and/or setup/teardown methods is
something I'd like to do as soon as we get some of that mythical
"downtime" between releases. :)

Cheers,
Liz

On 4/5/13 12:43 PM, "holger krekel" <holger at merlinux.eu> wrote:

>Hi Liz,
>
>below and attached is a solution which should fullfill your expectations
>and reports this on py.test --collectonly:
>
>    <Module 'test_params.py'>
>      <Function 'test_example[std-blue-cat]'>
>      <Function 'test_example[std-pink-dog]'>
>      <Function 'test_example[pro-blue-cat]'>
>      <Function 'test_example[pro-pink-dog]'>
>      <Function 'test_something[std]'>
>      <Function 'test_old_style[0]'>
>      <Function 'test_old_style[1]'>
>
>minor deviation:  The last two old-style ones are parametrized with your
>intended parameters, but the reported test id is collapsed.
>
>Lastly, i presume you are aware of @pytest.mark.parametrize, right?
>Your @params decorator looks like a version requiring more typing
>than neccessary to me :)
>
>HTH,
>holger
>
>
>import pytest
>
>def pytest_generate_tests(metafunc):
>    hasmyfixture = "myfixture" in metafunc.fixturenames
>    paramlist = getattr(metafunc.function, "paramlist", None)
>
>    if hasmyfixture:
>        argvalues = ["std"]
>        if paramlist:
>            argvalues.append("pro")
>        metafunc.parametrize("myfixture", argvalues, indirect=True)
>
>    if paramlist:
>        if isinstance(paramlist[0], dict):
>            # old-style
>            for p in paramlist:
>                metafunc.addcall(funcargs=p)
>        else:
>            assert isinstance(paramlist[0], list)
>            argnames = paramlist[0]
>            argvalues = [d["argvalues"] for d in paramlist[1:]]
>            metafunc.parametrize(argnames, argvalues)
>
>def params(decolist):
>    def wrapper(function):
>        function.paramlist = decolist
>        return function
>    return wrapper
>
>class SomeObject:
>    def close(self):
>        print "closing"
>
>class SomeOtherObject(SomeObject):
>    pass
>
>@pytest.fixture
>def myfixture(request):
>    if request.param == 'std':
>        myfix = SomeObject()
>    elif request.param == 'pro':
>        myfix = SomeOtherObject()
>    else:
>        assert 0, "unknown param"
>    def fin():
>        myfix.close()
>    request.addfinalizer(fin)
>    return myfix
>
>@params([
>    ['color', 'type'],
>    { 'argvalues': [ 'blue', 'cat'] },
>    { 'argvalues': ['pink', 'dog'] }
>])
>def test_example(myfixture, color, type):
>    # this is the new test we want to add
>    assert 0
>
>def test_something(myfixture):
>    # existing test which only uses std fixture
>    assert 0
>
>@params([
>    {'arg1': 1, 'arg2': 2},
>    {'arg1': 3, 'arg2': 5}
>])
>def test_old_style(arg1, arg2):
>    # existing tests which don't use fixtures
>    assert 0
>
>
>On Fri, Apr 05, 2013 at 17:08 +0000, Elizabeth Lin wrote:
>> Hi Holger,
>> 
>> Thanks for responding! Comments inline below.
>> 
>> On 4/4/13 11:14 PM, "holger krekel" <holger at merlinux.eu> wrote:
>> 
>> >Hi Elizabeth,
>> >
>> >And sorry for taking a while but i am still not sure i fully
>>understand.
>> >I am pretty sure we can find a solution but i'd like to find one that
>> >fits the problem :)
>> >
>> >Could you clarify your problem purely from the test function side?
>> >Particularly, if you have this test:
>> >
>> >    @params([
>> >        ['color', 'type'],
>> >        { 'argvalues': [ 'blue', 'cat'] },
>> >        { 'argvalues': ['pink', 'dog'] }
>> >    ])
>> >    def test_example(myfixture, color, type):
>> >        # this is the new test we want to add
>> >        assert 0
>> >
>> >do i understand it right that ``myfixture`` should be indirectly
>> >created by using ["std", "pro"] as respective parameters because
>> >there is a @params decorator? And that for
>> 
>> Yes, myfixture should be indirectly created when we call
>> metafunc.parametrize to pass in ["std", "pro"] as parameters, but only
>>for
>> specific tests - in this case the tests generated should be:
>> - std, blue, cat
>> - std, pink, dog
>> - pro, blue, cat
>> - pro, pink dog
>> 
>> >
>> >    def test_something(myfixture):
>> >        # existing test which only uses std fixture
>> >        assert 0
>> >
>> >you only want ``myfixture`` created with the "std" parameter?
>> 
>> Yes, that's correct.  So only test should be
>> - std
>> 
>> >
>> >And that for:
>> >
>> >    @params([
>> >        {'arg1': 1, 'arg2': 2},
>> >        {'arg1': 3, 'arg2': 5}
>> >    ])
>> >    def test_old_style(arg1, arg2):
>> >        # existing tests which don't use fixtures
>> >        assert 0
>> >
>> >you don't want any "myfixture" created at all?
>> 
>> Also correct.  Generated tests should be
>> - 1, 2
>> - 3, 5
>> 
>> Cheers,
>> Liz
>> 
>> >
>> >
>> >cheers,
>> >holger
>> >
>> >
>> >
>> >On Thu, Apr 04, 2013 at 22:51 +0000, Elizabeth Lin wrote:
>> >
>> >> Hi, 
>> >> 
>> >> I have some tests which I'd like to parametrize using both more
>>complex
>> >> fixtures as well as simple string arguments.  How are folks doing
>>this
>> >> currently?  Or is this a use case that hasn't been seen before?
>>Using
>> >> metafunc.parametrize in a pytest_generate_test hook won't work for me
>> >> since I need the fixtures to have indirect=True to pass the argname
>>as a
>> >> request.param, but the other arguments to have indirect=False.
>> >> 
>> >> For example, if I have a test fixture and test case which looks like
>>the
>> >> following:
>> >> Any suggestions for how to accomplish this would be much appreciated!
>> >> 
>> >> 
>> >> def pytest_generate_tests(metafunc):
>> >> 	if metafunc.function.__name__ == 'test_example':
>> >>         	argnames = []
>> >>         	argvalues = []
>> >> 		parameters = getattr(metafunc.function, 'paramlist', ())
>> >> 		for p in parameters:
>> >> 			if type(p) == list:
>> >> 				argnames = tuple(['myfixture'] + p)
>> >> 			else:
>> >> 				argvalues.append = tuple(['std'] + p['argvalues'])
>> >> 				argvalues.append = tuple(['pro'] + p['argvalues'])
>> >> 		# I want to do the following, but it won't work since some of the
>> >> 		# args need indirect set to true
>> >> 		# and some need indirect set to false.
>> >> 		metafunc.parametrize(argnames, argvalues, indirect=True)
>> >> 	elif 'myfixture' in metafunc.fixturenames:
>> >> 		# we have existing tests which use the fixture, but only with std
>> >> 		metafunc.parametrize("myfixture", "std")
>> >> 	else:
>> >> 		# we have existing tests which use older style parametrization,
>> >> 		# non-fixture
>> >> 		for p in getattr(metafunc.function, 'paramlist', ()):
>> >> 			metafunc.addcall(funcargs=p)
>> >> 
>> >> 
>> >> def params(decolist):
>> >> 	def wrapper(function):
>> >> 		function.paramlist = decolist
>> >> 		return function
>> >> 	return wrapper
>> >> 
>> >> @pytest.fixture
>> >> def myfixture(request):
>> >> 	if request.param == 'std':
>> >> 		myfix = SomeObject()
>> >> 	elif request.param == 'pro':
>> >> 		myfix = SomeOtherObject()
>> >> 	def fin():
>> >> 		myfix.close()
>> >> 	request.addfinalizer(fin)
>> >> 	return myfix
>> >> 
>> >> @params([
>> >>     ['color', 'type'],
>> >>     { 'argvalues': [ 'blue', 'cat'] },
>> >>     { 'argvalues': ['pink', 'dog'] }
>> >> ])
>> >> def test_example(myfixture, color, type):
>> >>     # this is the new test we want to add
>> >> 
>> >> def test_something(myfixture):
>> >>     # existing test which only uses std fixture
>> >> 
>> >> @params([
>> >>     {'arg1': 1, 'arg2': 2},
>> >>     {'arg1': 3, 'arg2': 5}
>> >> ])
>> >> def test_old_style(arg1, arg2):
>> >>     # existing tests which don't use fixtures
>> >> 
>> >> 
>> >> Thanks for reading through this! I know it's rather long.
>> >> 
>> >> Cheers,
>> >> Liz
>> >> 
>> >> 
>> >> 
>> >> 
>> >> _______________________________________________
>> >> testing-in-python mailing list
>> >> testing-in-python at lists.idyll.org
>> >> http://lists.idyll.org/listinfo/testing-in-python
>> >> 
>> 




More information about the testing-in-python mailing list