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
Let's say you you have a test in package A which requires a fixture from package B. Currently one can import the fixture into the test or conftest.py and that works just fine.
However if the fixture is a modular fixture (i.e. one which depends on other fixtures), it will only work:
If the test imports that fixture's dependencies
Or, if the fixture's dependencies are plug-ins
Why doesn't pytest doesn't let modular fixtures use the import mechanism to gain access to their dependencies? My main gripes with having to use the plugin mechanism are:
It's not consistent with simpler use cases where imports are sufficient
It doesn't promote writing modular fixtures
It encourages one to use poor style when writing modular fixtures (i.e. implicitly relying on plugins to provide certain fixtures instead of using direct imports to explicitly state your intent)
Any thoughts about this? Or is there another way to go about this issue?
fixtures can be overridden by plugin order, by making them imported dependencies a major feature would be lost (namely being able to override/switch dependencies
What I'm suggesting here is really not at odds with the current system of dependency injection. What is currently in place is a hierarchy of scopes used to resolve fixture requests. From the pytest documentation, the current hierarchy is:
The discovery of fixtures functions starts at test classes, then test modules, then conftest.py files and finally builtin and third party plugins.
I'm suggesting that for a given fixture declared in some module, the search should extend beyond the final third-party scope and into the module itself.
It wouldn't break anything. One could always override it at any of the many scopes preceding it in the current hierarchy.
Could you elaborate a bit? In your first message, you mention "package A" and "package B", and in your follow up you mention "the search should extend beyond the final third-party scope and into the module itself".
I'm not sure I understand fully what you are proposing (one example or two would be helpful).
test_thing currently errors because I didn't import another_important_fixture into test.py.
What I'm suggesting is that the final, outermost scope for resolving another_important_fixture should be important_package.fixtures since that's the module from which important_fixture, which depends on it, originates. Currently the search for fixtures stops at the plugin scope.
I understand that the error might be intentional, but I'm suggesting that extending the search to important_package.fixtures in the above example would encourage modularity and fixture reuse (outlined in my original comment) and wouldn't conflict with the existing uses of pytest.
As it stands, the only way to use modular fixtures at scale involves using plug-ins, since it doesn't make sense for individual tests to import higher-order dependencies (i.e. the dependencies of dependencies).
for a fixture a python import is a definition of an entirely new and different fixture so to begin with, it makes no sense to import a single fixture, as its not an import of a fixtur its a definition of a fixture
Also I really like how one can define a fixture in a test module, and when it becomes useful for other test modules the fixture can just be moved to a conftest.py file and no test code need to change. If fixtures were imported explicitly, that would mean that moving a fixture I would need to change imports around when moving a fixture to higher-level in the module hierarchy for reuse. I also understand that this would still be possible under your proposal, just thought I would mention it.
Also I'm not sure it would be possible to implement this in backward compatible way. As @RonnyPfannschmidt mentioned, in the current design when you import a fixture into a module, it is exactly as if you were defining a new fixture in that scope with the same name. This is actually a common source of errors for users, so much we have tried to figure out ways to warn about it (#1511).
Thanks @RonnyPfannschmidt for the explanation and thanks @nicoddemus for the helpful example. I didn't realize that one could, in essence, baptize another module as a plugin (this is documented, but I guess I misunderstood it as only applying to plugins (i.e. modules with declared entry points)). It seems that this also recursively processes pytest_plugins in the newly baptized plugin so that higher-order dependencies can be handled that way (that is, if important_package.fixtures in your example also contains a pytest_plugins variable, that gets processed as well). This is not obvious from the documentation either - the documentation only mentions this effect for conftest.py
Imports acting as redefinitions of fixtures is most likely a common source of errors because it's not documented (at least in the fixtures documentation - the one thing that people might want to reuse between tests) and it runs contrary to the normal functioning of Python imports. I'm guessing it's being done for cleanup and isolation between tests. Anyhow, I can see a sane way forward now when it comes to sharing fixtures from other packages, so thank you.
Activity
RonnyPfannschmidt commentedon Feb 14, 2017
fixtures can be overridden by plugin order, by making them imported dependencies a major feature would be lost (namely being able to override/switch dependencies
fixtures are a "dependency injection" system
TristenHayfield commentedon Feb 14, 2017
What I'm suggesting here is really not at odds with the current system of dependency injection. What is currently in place is a hierarchy of scopes used to resolve fixture requests. From the pytest documentation, the current hierarchy is:
I'm suggesting that for a given fixture declared in some module, the search should extend beyond the final third-party scope and into the module itself.
It wouldn't break anything. One could always override it at any of the many scopes preceding it in the current hierarchy.
nicoddemus commentedon Feb 14, 2017
Hi @TristenHayfield,
Could you elaborate a bit? In your first message, you mention "package A" and "package B", and in your follow up you mention "the search should extend beyond the final third-party scope and into the module itself".
I'm not sure I understand fully what you are proposing (one example or two would be helpful).
TristenHayfield commentedon Feb 14, 2017
Oops I meant the third-party plugin scope.
Assume you have some test which is trying to make use of a fixture provided by another package :
test.py:
important_package.fixtures:
test_thingcurrently errors because I didn't importanother_important_fixtureinto test.py.What I'm suggesting is that the final, outermost scope for resolving
another_important_fixtureshould beimportant_package.fixturessince that's the module from whichimportant_fixture, which depends on it, originates. Currently the search for fixtures stops at the plugin scope.Does that make sense?
RonnyPfannschmidt commentedon Feb 14, 2017
nope, the error you see is intentional,
the main problem is actual importing of fixtures (that makes them seen as a new fixture at a different place)
you either have to do that for all of them or none
TristenHayfield commentedon Feb 14, 2017
I understand that the error might be intentional, but I'm suggesting that extending the search to
important_package.fixturesin the above example would encourage modularity and fixture reuse (outlined in my original comment) and wouldn't conflict with the existing uses of pytest.As it stands, the only way to use modular fixtures at scale involves using plug-ins, since it doesn't make sense for individual tests to import higher-order dependencies (i.e. the dependencies of dependencies).
RonnyPfannschmidt commentedon Feb 14, 2017
for a fixture a python import is a definition of an entirely new and different fixture so to begin with, it makes no sense to import a single fixture, as its not an import of a fixtur its a definition of a fixture
nicoddemus commentedon Feb 15, 2017
FWIW, it is easy to turn any module into a plugin. Using the example you posted all you need is:
test.py:
Also I really like how one can define a fixture in a test module, and when it becomes useful for other test modules the fixture can just be moved to a
conftest.pyfile and no test code need to change. If fixtures were imported explicitly, that would mean that moving a fixture I would need to change imports around when moving a fixture to higher-level in the module hierarchy for reuse. I also understand that this would still be possible under your proposal, just thought I would mention it.Also I'm not sure it would be possible to implement this in backward compatible way. As @RonnyPfannschmidt mentioned, in the current design when you import a fixture into a module, it is exactly as if you were defining a new fixture in that scope with the same name. This is actually a common source of errors for users, so much we have tried to figure out ways to warn about it (#1511).
TristenHayfield commentedon Feb 15, 2017
Thanks @RonnyPfannschmidt for the explanation and thanks @nicoddemus for the helpful example. I didn't realize that one could, in essence, baptize another module as a plugin (this is documented, but I guess I misunderstood it as only applying to plugins (i.e. modules with declared entry points)). It seems that this also recursively processes
pytest_pluginsin the newly baptized plugin so that higher-order dependencies can be handled that way (that is, ifimportant_package.fixturesin your example also contains apytest_pluginsvariable, that gets processed as well). This is not obvious from the documentation either - the documentation only mentions this effect for conftest.pyImports acting as redefinitions of fixtures is most likely a common source of errors because it's not documented (at least in the fixtures documentation - the one thing that people might want to reuse between tests) and it runs contrary to the normal functioning of Python imports. I'm guessing it's being done for cleanup and isolation between tests. Anyhow, I can see a sane way forward now when it comes to sharing fixtures from other packages, so thank you.
Improve pytest_plugins docs
nicoddemus commentedon Feb 15, 2017
Sure, glad we could help!
Opened #2252 attempting to improve the docs a little.