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" <[email protected]> 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" <[email protected]> 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 >> >> [email protected] >> >> http://lists.idyll.org/listinfo/testing-in-python >> >> >> _______________________________________________ Pytest-dev mailing list [email protected] http://mail.python.org/mailman/listinfo/pytest-dev
