[TIP] fixture ordering and dynamically adding fixtures to Testcases

arun kali raja arunsep886 at gmail.com
Tue Jan 16 08:40:40 PST 2018

Hi Bruno/Floris,

Thanks a lot for the insight.

i was trying out few things and found these..

1. Ordering fixtures:
     Suppose the user wants to impose the ordering of fixtures in a certain
way then he can do it via the *pytest_generate_tests(metafunc)* hook:

    the *metafunc* object has a member variable *fixturenames*.. that is
the list of fixtures that are gona be executed for this test.

    the user can reorder it as he wishes.

    In my case i have some autoUse fixtures, but they have some dependency
fixtures  which are not autoUse.. so fixtureA(autoUse) is dependent on
fixtureB( not autoUse). So only if fixtureB is available i want to include
fixtureA otherwise i dont want to use fixtureA at all.. i Can achieve this
by checking contents of the list and making appropriate decisions to remove
invalid fixtures too..

   Now say i want to call all my session fixtures , then module, then class
and then function fixtures, how do i know which is what? bcoz the
*fixturenames* variable is just a list of fixture names(strings)..

   the solution is to check the fixture information from the
*dictionary. the dictionary has fixture name as key and a tuple as a value.
the tuple has a object FixtureDef which scope related information.

    Is this a right way to do? or just a hacky way to make things work?

Curious question:
   why is the values in the *_arg2fixturedefs* a tuple?? can a fixture have
more than one definitions??

2. Adding fixtures dynamically at run time.

    I am trying to create a library of fixtures which will provide a
standard set of functionalities.. the user has to add those in his testcase
and then parameterise them according to his usecase.

    Instead of the selecting the fixtures i want to give flexibility to
specify the usecase in a marker with some parameters and then the
dynamically add the fixtures.

    I think here too *pytest_generate_tests* is the right place to do such

    I was able to read the markers using *metafunc.function*  member
variable and then add fixtures to the *metafunc.fixturenames* member

    And the fixture got called at the time of execution too..

        Suppose i want to create a fixture chain according to my use case i
can do it by inserting them one after the other in the *fixturenames. *But
suppose i want to access value returned by fixtureA in fixtureB i am not
sure how to do it.. Becase in that case i have to add fixtureA as a
dependency to fixtureB. I havent figured out that yet..

    Curious Question:
       The *metafunc._arg2fixturedefs *variable i have mentioned above
seems to be populated at the *pytest_generate_tests *phase itself.. I am
not sure how appending the fixture name at *pytest_generate_tests *even
works.. i tried putting a breakpoint at *pytests_itemcollected() *hook..
but even there it seems that the *_arg2fixturedefs *doesnt  seem to have
the entry for the newly added fixture.. So i am not sure yet how it works..

Arun Kaliraja.B

On 16 January 2018 at 02:47, Floris Bruynooghe <flub at devork.be> wrote:

> Hi,
> It seems Bruno's reply got lost somewhere...
> On Sat 13 Jan 2018 at 22:38 +0530, arun kali raja wrote:
> > 1. I have a .py file where i have defined two Autouse fixtures..
> > fixtureMod_1 is module scoped and fixtureFunc_1 is function scoped.. Now
> in
> > conftest.py i have imported these two fixtures.. in my test_x.py i have a
> > another module scoped fixture say fixtureMod_2..The order of execution of
> > fixture seems to be fixtureMod_1,fixtureFunc_1 and then fixtureMod_2..
> isnt
> > module level fixtures supposed to be executed before function level
> > fixtures?? isnt that how scopes are ordered.. i am able to solve the
> issues
> > by giving fixtureMod_2  as a dependency to fixtureFunc_1.. but still i am
> > trying to understand why pytest respects the scope ordering in this
> case..
> > does the order of sourcing the fixture override the scope based
> ordering??
> If I understand this correct then you have something like this:
> foo.py:
>    @pytest.fixture
>    def fixtureMod_1():
>        pass
>    @pytest.fixture
>    def fixtureFunc_1():
>        pass
> conftest.py:
>    from foo import fixtureMod_1
>    from foo import fixtureFun_1
> test_x.py:
>    @pytest.fixture
>    def fixtureMod_2():
>        pass
>    def test_foo(fixtureMod_1, fixtureFunc_1, fixtureMod_2):
>        pass
> From pytest's point of view you have not specified any ordering here.
> The scopes are used to know when the setup and teardown of each fixture
> needs to run, e.g. with a function-scope the fixture will be created for
> just before a single test which needs it and destroyed right after this
> test has run.
> For a module scope the fixture gets only created once for
> all tests in a module.  So if multiple tests in test_x require either
> fixtureMod_1 or fixtureMod_2 their setup and teardown will only be
> executed once.
> So scopes have nothing to do with ordering.
> As you discovered if you declare dependencies on fixtures then the
> ordering will start to be defined:
> @pytest.fixture
> def fixtureFunc_1(fixtureMod_2):
>     pass
> This will ensure that fixtureMod_2 is created first.  It's good that
> this dependency needs to be explicit in your code.  However be careful
> in general to not over-use dependencies like this, e.g. if the only
> reason you declare fixtureMod_2 is to use as a dependency in
> fixtureFunc_1 then maybe fixtureMod_2 shouldn't be a fixture at all.
> As another aside btw, I always *strongy* advice against ever importing
> fixtures.  The semantics of it are very confusing (this was probably a
> design mistake) so instead always move the fixture to the right
> location, i.e. your conftest.py file.
> > 2. I have one usecase where i want to dynamically create a fixture
> chain..
> > say i mark by testcase with useCaseA and that translates to an ordering
> of
> > fixture.. can i add a fixture to a testcase at run time.. at
> > pytest_generate_test phase or pytest_configure phase?? i found a method
> > called add_marker to add a marker at runt time.. since fixtures can also
> be
> > added via markers i was trying to explore if can use that method but it
> > doesnt accept any parameters.. any way i can achieve this??
> Here I'm somewhat lost with your example, I think you have this:
> @pytest.mark.useCaseA
> def test_foo():
>     pass
> But I'm afraid you'll have to give me more info on how tha translates to
> fixtures in your implementation.
> With respect to dynamically requested fixtures, this is usually done
> using request.getfixturevalue() [0].  However, and this is where
> personal opinions differ, my advice is to avoid this if you possibly
> can.  And my opinion is also that this is always possible, e.g. a
> fixture can return instances of two different classes depending on the
> dynamic "thing".
> Hope this helps,
> Floris
> [0] https://docs.pytest.org/en/latest/builtin.html?highlight=
> getfixturevalue#_pytest.fixtures.FixtureRequest.getfixturevalue

பா. அருண் காளி ராசா
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.idyll.org/pipermail/testing-in-python/attachments/20180116/76908e0e/attachment.htm>

More information about the testing-in-python mailing list