pytest Fixtures and Plugin Interoperability: Managing Session-Scoped State
0 reputation · 13 Aug 2020, 20:21 UTC
0 reputation · 13 Aug 2020, 20:21 UTC
When integrating custom pytest plugins with session-scoped fixtures, there is a potential for state leakage across disparate test modules. While pytest ensures that session-scoped fixtures persist for the duration of the test run, the interaction between plugin-defined fixtures and local conftest.py definitions can create implicit dependencies.
The primary goal is to ensure that session-level state managed by a plugin remains isolated or predictably reset when multiple plugins attempt to modify the same global resource. This becomes complex when parameterized fixtures are used to inject different configurations into a shared session resource.
conftest.py both define a session-scoped fixture with the same name?34775 reputation · 14 Aug 2020, 06:04 UTC
In pytest, naming conflicts between plugins and local configuration files are resolved via a hierarchical lookup. When a plugin and a local conftest.py both define a session-scoped fixture with the same name, the local conftest.py definition takes precedence. The local fixture overrides the plugin's version for all tests within that directory subtree and its descendants.
Regarding teardown, pytest utilizes a Last-In-First-Out (LIFO) sequence based on the dependency graph. If a session-scoped plugin fixture depends on a session-scoped local fixture, the local fixture is initialized first. Consequently, the plugin fixture is torn down before the local fixture is released.
Pytest searches for fixtures in the following order of priority:
conftest.py closest to the test file.conftest.py files moving up the directory tree.Because the local conftest.py is higher in the hierarchy than the plugin, it effectively masks the plugin's implementation. This can lead to state leakage or failures if the plugin's internal logic relies on the specific implementation of its own fixture.
The teardown order is strictly the inverse of the setup order. For session-scoped resources:
To verify the resolution and teardown behavior in your specific environment (assuming pytest 7.0+), use the following methods:
Run the following command to see which fixture implementation is being used for a specific test module:
pytest --fixtures
Implement yield fixtures with print statements to confirm the LIFO sequence:
# conftest.py (Local)
import pytest
@pytest.fixture(scope="session")
def shared_resource():
print("\n[Setup] Local Resource")
yield "local_val"
print("\n[Teardown] Local Resource")
# plugin.py (Plugin)
@pytest.fixture(scope="session")
def plugin_resource(shared_resource):
print("\n[Setup] Plugin Resource")
yield "plugin_val"
print("\n[Teardown] Plugin Resource")
Expected Output: Setup Local $\rightarrow$ Setup Plugin $\rightarrow$ Teardown Plugin $\rightarrow$ Teardown Local.
Note: To provide a more specific recommendation on state isolation, please clarify if you are using pytest_generate_tests for the parameterization of these session fixtures.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.