-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
5.3.3 fixture order wrong with 'trylast' #6540
Comments
Thanks @lhupfeldt for taking the time to report and produce a MWE. This is definitely related to #6492, although it might provide new clues if this is a problem in pytest or wrong assumptions. cc @SalmonMode |
@lhupfeldt @nicoddemus As far as I can tell, this is the result of non-deterministic fixture order, which isn't a bug on pytest's end. In the example provided, the fixtures are defined in such a way that linearization isn't possible in a consistent way, so the order they occur in is up to the whims of python at that point. This is what's causing the intermittent errors vs failures. I saw two variations when running pytest 5.3.3 with the
And here's the other:
Notice how The only way you'll get the failure instead of the error is if |
@SalmonMode thanks for digging in!
But why this behavior only happens in 5.3.3 and not other versions? Is there anything that can be changed in the provided code to avoid this difference? |
Is this related to bab4dd0? (or just something similar) |
@blueyed and @MarcSchmitzer found an issue elsewhere that could have been exacerbating the non-determinism in that sets are not iterated over in the same order they are added to (#6512 (comment) and bab4dd0). In general though, changing the sets back to lists in the part of pytest that's relevant won't guarantee a fix for the issue presented in this example code. To fix it for sure, any fixture that depends on another fixture being completed before it runs should request the fixture it depends on. So if fixture A needs fixture B to be done before fixture A starts, then fixture A should request fixture B. |
Applying that change over
I agree, but in any case bab4dd0 turned that process at least deterministic. 👍 |
Maybe I misunderstood the meaning of 'trylast' but I thought that should
ensure the order I has before 5.3.3. If that is not the case then another
feature is really needed for this.
I will be mostly offline for some days so replys will be slow.
…On Fri, 24 Jan 2020, 00:29 Bruno Oliveira, ***@***.***> wrote:
Is this related to bab4dd0
<bab4dd0>?
(or just something similar)
Applying that change over 5.3.3 then I always get the same --setup-plan,
so this might explain why 5.3.3 was producing non-deterministic results.
To fix it for sure, any fixture that depends on another fixture being
completed before it runs should request the fixture it depends on.
I agree, but in any case bab4dd0
<bab4dd0>
turned that process at least deterministic. 👍
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#6540?email_source=notifications&email_token=AAMCZSOJ4CAGXFHHRQ5OG3DQ7IR4DA5CNFSM4KKN2Y62YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJZHEQA#issuecomment-577925696>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAMCZSINQMPNUSBZY6GRFUTQ7IR4DANCNFSM4KKN2Y6Q>
.
|
@lhupfeldt If that's what you want, then you just need to make sure you define a fixture which requests the last of your generated fixtures (since they seem to happen in sequence), and have the first of your generated fixtures request the other, normal fixtures in the scope that's relevant. Or you could rely on scope itself, e.g. make a single test file for a single test case, then have all fixtures be for the module scope level, and then, make the fixture you want to happen last have a class scope level. This approach would work at different scopes too. |
@SalmonMode is correct, if there's an implicit ordering required, it is best to make that order explicit. Either way, it seems bab4dd0 at least makes this deterministic and we should produce a test with implicit dependencies which fails without bab4dd0, so that at least we will know if we ever break this order again in the future (rather than having users finding this out). |
Thanks, that's what I meant. 👍 |
@SalmonMode
I took a look at my code and just realized that 'trylast' is on the hook
only, not on the 'create_register_triggers' fixture. What I want is to have
trylast functionality there as well.
My actual test suite is not as simple as the example. I have multiple
independent sets of dbdata loading fixtures, all generated from comments
embedded in SQL files. To model the behaviour which I saw in pre 5.3.3
using fixture dependencies, would mean that every addition/removal of a
dbdata load fixture might require an update of the
'create_register_triggers' fixture. The only reasonable solution wold then
be to also generate that dynamically.
Another reasonable assumption about evaluation order of independent
fixtures, could be that fixtures given later in parameter list are invoked
later. Was this what was happening before 5.3.3?
…On Fri, 24 Jan 2020, 19:43 Bruno Oliveira, ***@***.***> wrote:
Thanks, that's what I meant. 👍
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#6540?email_source=notifications&email_token=AAMCZSMV23GZ62525VS3XSTQ7MZFBA5CNFSM4KKN2Y62YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJ3WWNY#issuecomment-578251575>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAMCZSJSB4LA63HPMBJV7I3Q7MZFBANCNFSM4KKN2Y6Q>
.
|
It might be possible that the order a fixture lists other fixtures in its signature, but I'm not positive. That said, if it was happening, it would be a move that was as similarly risky as relying on the order fixtures are defined in a module to determine the order they run in. Anything else could come along and shuffle that order, making it extremely fragile. Having However, you could go the opposite direction. Pytest will try to bring |
I just realised that sinse I have many different incompatible sets of
dbdata fixtures, making'create_register_triggers' depend on those is not an
option. I would need different instances of that depending on the dbdata
loaded.
I disagree with the 'trylast' being fragile. It will just mean that if you
have independent sets of fixtures, then a set including any fixture with
trylast will be executed after a set without trylast. Within a set the
dependency order will be used.
…On Fri, 24 Jan 2020, 23:56 Chris NeJame, ***@***.***> wrote:
It might be possible that the order a fixture lists other fixtures in its
signature, but I'm not positive. That said, if it was happening, it would
be a move that was as similarly risky as relying on the order fixtures are
defined in a module to determine the order they run in. Anything else could
come along and shuffle that order, making it extremely fragile.
Having trylast also be usable on fixtures would be fragile as well in a
similar way, as other fixtures could have that marker causing a conflict,
or other fixtures could explicitly request the marked fixture, meaning it
wouldn't actually be the last fixture to run, as it would have to run
before the fixtures that requested it.
*However*, you *could* go the opposite direction. Pytest will try to
bring autouse to the front of the line, so you could make all those dbdata
fixtures autouse to give a similar level of assurance that the the
fixture you want to happen last will at least happen after them. But, of
course, if another autouse fixture requests it, it will effectively
become autouse as well (which is what makes it a similar level of
assurance).
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#6540?email_source=notifications&email_token=AAMCZSLYP7XICA74TGW26GLQ7NWYZA5CNFSM4KKN2Y62YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJ4K4RQ#issuecomment-578334278>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAMCZSIZS7LJOETVQY4LJADQ7NWYZANCNFSM4KKN2Y6Q>
.
|
When I say "fragile", I mean the order of fixtures is not being completely controlled/coordinated, and is relying on things happening to work out. The logic of While this would certainly have value when coordinating priority (although there's probably a better name than Even if you have conflicting dependencies based on context for Basically, you could have a fixture that does literally nothing, and is just called @pytest.fixture
def post_dbdata(dbdata23):
pass Then, you can define your |
@SalmonMode There is no 'last one'. |
I agree that having some sort of "I must happen before this fixture" feature would be useful. You might want to make that feature request. In the meantime, you could still leverage the general idea of deps = ",".join(f"dbdata{i}" for i in range(23))
exec(f"""
@pytest.fixture(scope='function')
def post_dbdata({deps}):
pass
""") This would also allow you to modify it in place based on context, so if you want a particular test class to have different dependencies, you can override it inside that test class. |
@nicoddemus I think this one can be closed, as there was no bug with |
@blueyed @SalmonMode |
@lhupfeldt the fix is incoming (eventually ™️ ). Here's the specific line that resolves it https://github.com/pytest-dev/pytest/pull/6551/files#diff-6725679ae3a6464a250386ddaec4b18bR989. In 5.3.3, it was a set, so it wasn't being iterated over in the same order that things were inserted in. We were sort of using #6492 as a catch all for all bugs related to 5.3.3, so things got a tad messy. There was further discussion about the iteration order here #6512. Sorry for the confusion. I should have linked both initially. |
@SalmonMode |
@lhupfeldt it would be great to get more test cases for the failures this caused. With regard to random ordering we have merged a test already though: #6554. |
Test fails randomly during setup because of bad fixture order after upgrading to 5.3.3.
A fixture marked 'trylast' is being called too early.
It fails randomly only about 50% of the time.
Test run stable with 5.3.2 and 5.3.4.
Example attached (reduced from some complex database setup).
Note that the example also uses dynamically generated fixtures, I'm not sure if this is part of the issue.
fixture_order.zip
Output from a run failed because of fixture called too early:
Output from run using 5.3.4 with correct fixture invocation order (explicitly failed to show setup):
The text was updated successfully, but these errors were encountered: