You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Originally reported by: BitBucket: javex, GitHub: javex
I have two fixtures on on a class, like this:
class TestMyThing(object):
@pytest.fixture(autouse=True)
def _fixture_0(self):
self.data = ...
@pytest.fixture(autouse=True)
def _fixture_1(self):
data = self.data
...
The problem here is that _fixture_1 needs _fixture_0 to be run before _fixture_1 as it needs self.data to be set. I'd really like to split these two up instead of using a single fixture as they are fairly complex. They are also only used on this particular class, so I'd prefer to not have it outside of my class for organizational purposes.
However, if I name the fixtures in such a way that lexicographic ordering is the other way around, they will not be called in the right order. I guess that pytest orders fixtures somehow internally (or maybe it is internal ordering of python?).
Anyways, I'd like some deterministic way of fixture ordering to be documented or configurable. One way I could imagine would be to just document that the will always be lexicographically ordered, thus you can be sure that my above naming would always be in the correct order.
Another way would be some kind of mechanism by which I can require another class fixture, since I would order other fixtures this way by declaring one requires the other.
Do you think such a feature would be possible? Or do you think it is a very special application that doesn't deserve it?
Original comment byJurko Gospodnetić (BitBucket: jurko, GitHub: jurko):
Don't know exactly how pytest handles fixtures defined inside classes (never used those 😄), but my first thought was: 'Have you tried passing _fixture_0 as a parameter to _fixture_1 and then making _fixture_0 value accessible via _fixture_1?
See pytest docs for an example on how to use one fixture from another.
But trying to rewrite your code example to do this, I really have to wander - why? Something is missing in that example. Why is _fixture_0 a fixture at all? What does its function return? Why doesn't _fixture_1 return both things?
Could you perhaps post a more complete example? Unless you are absolutely certain your situation really needs fixed fixture call ordering and nothing else will suffice - in which case someone more knowledgeable about pytest internals will have to help you. 😢
Original comment byBitBucket: javex, GitHub: javex:
I am using something similar to autouse fixtures (xUnit setup on steroids). However, I found the params argument to be extremely useful to create a testing matrix and so I often use multiple fixtures that all have params and so they test any combination there is to test.
This combination is mainly what creates the problem. Here is a more complete example:
class TestMyThing(object):
@pytest.fixture(autouse=True, params=[0, 1, 2])
def _fixture_0(self, request):
self.parent = Parent()
self.children_count = request.param
for i in range(request.param):
self.parent.children.append(Child())
@pytest.fixture(autouse=True, params=['val1', 'val2'])
def _fixture_1(self, request):
self.expected_val = request.param
for child in self.parent.children:
child.value = request.param
def test_children(self):
assert len(self.parent.children) == self.children_count
for child in self.parent.children:
assert child.value == self.expected_val
It is a bit arbitrary but I hope it gets over the message: I want to test the matrix consisting of the params of _fixture_0 (which is a count of how many children to create) and those of _fixture_1 (what value should be assigned to a specific field on those children).
As a result I get the product of those two as testing cases and on each now test_children checks that the right amount exists and each child has the expected value. Don't bother with the details of the test here, it is extremely simplified to show the case.
Now I realize that I could just move the fixtures out of the class and have a single autouse fixture that requires those two, assigns them to self and then the test is run. I also realize that I could create the product matrix myself and use a single fixture with all code.
However, the code is fairly large and it only applies to this particular test class. So moving it out of the class means polluting the global namespace which I'd like to avoid. And merging all fixtures (currently 3) into one means creating the matrix myself.
I realize that this is probably some crazy-ass stuff that nobody ever intended to use but from my perspective it would be the cleanest solution if I could do something like before='_fixture_1' or requires='_fixture_0'. I think this would be cool, but I am also open to hear any solutions I might not have considered. If this is denied (which is okay for me), then I'd probably resort to polluting my global namespace in this module.
Original comment byJurko Gospodnetić (BitBucket: jurko, GitHub: jurko):
What I referred to in my original reply was something like this:
#!python
import pytest
class Child:
pass
class Parent:
def __init__(self):
self.children = []
class TestMyThing:
@pytest.fixture(autouse=True, params=[0, 1, 2])
def _fixture_02(self, request):
self.parent = Parent()
self.children_count = request.param
for i in range(request.param):
self.parent.children.append(Child())
@pytest.fixture(autouse=True, params=['val1', 'val2'])
def _fixture_01(self, request, _fixture_02):
self.expected_val = request.param
for child in self.parent.children:
child.value = request.param
def test_children(self):
assert len(self.parent.children) == self.children_count
for child in self.parent.children:
assert child.value == self.expected_val
I renamed your fixtures so they get instantiated in an 'invalid' order by default, and then got them to be instantiated in the correct order by explicitly marking the second one as using the first one (i.e. by passing the first one to the second one as an argument).
Although, I do not like the approach you take here. Too much seems to be done behind the scene. If I needed such a refactoring, I'd most likely do it something like this (disclaimer: naming chosen only to match the original example):
#!python
import pytest
class Child:
pass
class Parent:
def __init__(self):
self.children = []
class TestMyThing:
def _auto_fixture_helper_02(self, param):
self.parent = Parent()
self.children_count = param
for i in range(param):
self.parent.children.append(Child())
def _auto_fixture_helper_01(self, param):
self.expected_val = param
for child in self.parent.children:
child.value = param
@pytest.fixture(autouse=True, params=[(p02, p01)
for p02 in (0, 1, 2)
for p01 in ('val1', 'val2')])
def _auto_fixture(self, request):
param_02, param_01 = request.param
self._auto_fixture_helper_02(param_02)
self._auto_fixture_helper_01(param_01)
def test_children(self):
assert len(self.parent.children) == self.children_count
for child in self.parent.children:
assert child.value == self.expected_val
What I would like to see in pytest is a way to more easily define orthogonal fixture parametrizations. For example using separate decorators, the same as can be done for specific test cases using pytest.mark.parametrize:
That would allow you to refactor by assigning names to each of those decorators and reuse them where needed or simply use their names as something revealing your intent. But on the other hand, I don't think that would buy us much in this scenario and I do not have a more applicable scenario ready here so I guess it all just falls under the 'bikeshedding' category. 😄
Original comment byBitBucket: javex, GitHub: javex:
Wow, I did not know about parametrize, that is actually awesome! If this would be possible on fixtures instead of just single test functions, it would solve my problem. For now, I have taken the fixtures out of the class which seems to be the cleanest solution.
Original comment byBitBucket: javex, GitHub: javex:
Yeah, you say syntactic sugar but if the matrix is 2x3x3x2x2 then it quickly amounts to very ugly code. But you are right, what you show there is possible. I think we can close this issue since it seems to be a corner case that you can solve with the methods you describe. Thank you for your help :-)
Activity
pytestbot commentedon Mar 18, 2014
Original comment by Jurko Gospodnetić (BitBucket: jurko, GitHub: jurko):
Don't know exactly how pytest handles fixtures defined inside classes (never used those 😄), but my first thought was: 'Have you tried passing
_fixture_0as a parameter to_fixture_1and then making_fixture_0value accessible via_fixture_1?See pytest docs for an example on how to use one fixture from another.
But trying to rewrite your code example to do this, I really have to wander - why? Something is missing in that example. Why is
_fixture_0a fixture at all? What does its function return? Why doesn't_fixture_1return both things?Could you perhaps post a more complete example? Unless you are absolutely certain your situation really needs fixed fixture call ordering and nothing else will suffice - in which case someone more knowledgeable about pytest internals will have to help you. 😢
Best regards,
Jurko Gospodnetić
pytestbot commentedon Mar 18, 2014
Original comment by BitBucket: javex, GitHub: javex:
I am using something similar to autouse fixtures (xUnit setup on steroids). However, I found the
paramsargument to be extremely useful to create a testing matrix and so I often use multiple fixtures that all have params and so they test any combination there is to test.This combination is mainly what creates the problem. Here is a more complete example:
It is a bit arbitrary but I hope it gets over the message: I want to test the matrix consisting of the params of
_fixture_0(which is a count of how many children to create) and those of_fixture_1(what value should be assigned to a specific field on those children).As a result I get the product of those two as testing cases and on each now
test_childrenchecks that the right amount exists and each child has the expected value. Don't bother with the details of the test here, it is extremely simplified to show the case.Now I realize that I could just move the fixtures out of the class and have a single autouse fixture that requires those two, assigns them to
selfand then the test is run. I also realize that I could create the product matrix myself and use a single fixture with all code.However, the code is fairly large and it only applies to this particular test class. So moving it out of the class means polluting the global namespace which I'd like to avoid. And merging all fixtures (currently 3) into one means creating the matrix myself.
I realize that this is probably some crazy-ass stuff that nobody ever intended to use but from my perspective it would be the cleanest solution if I could do something like
before='_fixture_1'orrequires='_fixture_0'. I think this would be cool, but I am also open to hear any solutions I might not have considered. If this is denied (which is okay for me), then I'd probably resort to polluting my global namespace in this module.pytestbot commentedon Mar 19, 2014
Original comment by Jurko Gospodnetić (BitBucket: jurko, GitHub: jurko):
What I referred to in my original reply was something like this:
I renamed your fixtures so they get instantiated in an 'invalid' order by default, and then got them to be instantiated in the correct order by explicitly marking the second one as using the first one (i.e. by passing the first one to the second one as an argument).
Although, I do not like the approach you take here. Too much seems to be done behind the scene. If I needed such a refactoring, I'd most likely do it something like this (disclaimer: naming chosen only to match the original example):
What I would like to see in pytest is a way to more easily define orthogonal fixture parametrizations. For example using separate decorators, the same as can be done for specific test cases using
pytest.mark.parametrize:That would allow you to refactor by assigning names to each of those decorators and reuse them where needed or simply use their names as something revealing your intent. But on the other hand, I don't think that would buy us much in this scenario and I do not have a more applicable scenario ready here so I guess it all just falls under the 'bikeshedding' category. 😄
Hope this helps.
Best regards,
Jurko Gospodnetić
pytestbot commentedon Mar 19, 2014
Original comment by BitBucket: javex, GitHub: javex:
Wow, I did not know about
parametrize, that is actually awesome! If this would be possible on fixtures instead of just single test functions, it would solve my problem. For now, I have taken the fixtures out of the class which seems to be the cleanest solution.pytestbot commentedon Mar 19, 2014
Original comment by Jurko Gospodnetić (BitBucket: jurko, GitHub: jurko):
As I see it, there really is not much difference (except for some syntactic sugar) between:
and:
So I do not really see how having the first technique available would 'solve your original problem'.
Best regards,
Jurko Gospodnetić
pytestbot commentedon Mar 19, 2014
Original comment by BitBucket: javex, GitHub: javex:
Yeah, you say syntactic sugar but if the matrix is 2x3x3x2x2 then it quickly amounts to very ugly code. But you are right, what you show there is possible. I think we can close this issue since it seems to be a corner case that you can solve with the methods you describe. Thank you for your help :-)
pytestbot commentedon Mar 24, 2014
Original comment by holger krekel (BitBucket: hpk42, GitHub: hpk42):
Thanks everyone for the discussion. And I agree with closing the issue :)
Include license file in the generated wheel package (pytest-dev#484)