test_pluginmanager.py
Overview
This file contains a comprehensive suite of tests for the **`PytestPluginManager`** and its related plugin interaction mechanisms within the `pytest` testing framework. It validates the plugin manager's functionality such as plugin registration, hook management, plugin importing, environment variable consideration, plugin blocking, and various edge cases related to plugin lifecycle and configuration.
The tests ensure that plugins behave correctly with respect to hook specification additions, option parsing, plugin tracing, conftest imports, and plugin enable/disable operations. The file leverages `pytest` fixtures and utilities like `Pytester` for test isolation and simulation of plugin environments.
Detailed Explanation of Components
Fixture
pytestpm() -> PytestPluginManager
Purpose: Provides a fresh instance of
PytestPluginManagerfor use in tests.Returns: An instance of
PytestPluginManager.Usage Example:
def test_example(pytestpm: PytestPluginManager): assert isinstance(pytestpm, PytestPluginManager)
Class: TestPytestPluginInteractions
Contains tests focusing on interactions between plugins and the plugin manager, including hook additions, option parsing, configuration, hook proxying, tracing, and case sensitivity on file systems.
Methods:
test_addhooks_conftestplugin(self, pytester: Pytester, _config_for_test: Config) -> NoneTests adding new hook specifications from a conftest plugin and verifies hooks are called and return expected results.
Uses
pytest_addhooksto add hooks from a dynamically created module.Verifies the hook call returns the correct transformed value.
test_addhooks_nohooks(self, pytester: Pytester) -> NoneTests behavior when
pytest_addhookstries to add hooks from a module with no hooks.Expects a failure and error message indicating no hooks found.
test_do_option_postinitialize(self, pytester: Pytester) -> NoneEnsures that options added by a plugin are accessible on the config option object after plugin import.
test_configure(self, pytester: Pytester) -> NoneTests that plugins with
pytest_configurehooks are called during configuration.Confirms that multiple registrations trigger multiple configure calls.
Also tests unconfiguration behavior.
test_conftestpath_case_sensitivity(self, pytester: Pytester) -> NoneTests conftest import behavior on case-insensitive file systems (Windows).
Verifies that conftest files differing only by case are imported separately as distinct plugins.
test_hook_tracing(self, _config_for_test: Config) -> NoneTests the hook tracing mechanism that logs hook calls.
Verifies correct indentations and exception handling within traced hooks.
test_hook_proxy(self, pytester: Pytester) -> NoneTests the
gethookproxyfunctionality to obtain a hook proxy for a specific directory.Verifies different hook proxies are returned when plugins are imported from different conftest files.
test_hook_with_addoption(self, pytester: Pytester) -> NoneTests that hooks can be used inside
pytest_addoptionto dynamically determine default option values.Validates that the help message reflects the default value provided by the hook.
Standalone Functions
test_default_markers(pytester: Pytester) -> NoneRuns
pytest --markersand ensures default pytest markers liketryfirstandtrylastare listed.
test_importplugin_error_message(pytester: Pytester, pytestpm: PytestPluginManager) -> NoneTests that import errors in plugins provide useful traceback and error messages.
Ensures import errors are not swallowed and include plugin name and traceback context.
Class: TestPytestPluginManager
Tests core `PytestPluginManager` functionality including plugin registration, import, environment variable consideration, and plugin blocking.
Methods:
test_register_imported_modules(self) -> NoneVerifies registering a module works and that double registration raises
ValueError.
test_canonical_import(self, monkeypatch) -> NoneTests importing a plugin by canonical module name and ensures it is registered.
test_consider_module(self, pytester: Pytester, pytestpm: PytestPluginManager) -> NoneTests
consider_modulewhich loads plugins from a module'spytest_pluginsattribute.
test_consider_module_import_module(self, pytester: Pytester, _config_for_test: Config) -> NoneEnsures
consider_moduleimports plugins correctly and avoids double registration.
test_consider_env_fails_to_import(self, monkeypatch: MonkeyPatch, pytestpm: PytestPluginManager) -> NoneTests that environment variable plugins that can't be imported cause an
ImportError.
test_plugin_skip(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> NoneTests that plugins calling
pytest.skipat module level are gracefully skipped and reported.
test_consider_env_plugin_instantiation(self, pytester: Pytester, monkeypatch: MonkeyPatch, pytestpm: PytestPluginManager) -> NoneTests that plugins declared in
PYTEST_PLUGINSenvironment variable are loaded once and not duplicated.
test_pluginmanager_ENV_startup(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> NoneTests that plugins from environment variables are available during test runs.
test_import_plugin_importname(self, pytester: Pytester, pytestpm: PytestPluginManager) -> NoneTests importing plugins by simple and dotted module names, handling import errors correctly.
test_import_plugin_dotted_name(self, pytester: Pytester, pytestpm: PytestPluginManager) -> NoneTests importing plugins using dotted module names and accessing module attributes.
test_consider_conftest_deps(self, pytester: Pytester, pytestpm: PytestPluginManager) -> NoneTests error handling when considering conftest files that declare dependencies (
pytest_plugins) that cannot be resolved.
Class: TestPytestPluginManagerBootstrapping
Tests bootstrap and startup-related behaviors of the plugin manager, specifically plugin enabling/disabling and command-line parsing.
Methods:
test_preparse_args(self, pytestpm: PytestPluginManager) -> NoneTests parsing of command-line arguments related to plugin enabling/disabling (
-p).Validates handling of disabled plugins and errors when disabling essential plugins like
main.
test_plugin_prevent_register(self, pytestpm: PytestPluginManager) -> NoneTests that plugins marked as disabled via command line are not registered.
test_plugin_prevent_register_unregistered_already_registered(self, pytestpm: PytestPluginManager) -> NoneTests that already registered plugins can be unregistered if later disabled via command line.
test_plugin_prevent_register_stepwise_on_cacheprovider_unregister(self, pytestpm: PytestPluginManager) -> NoneTests that unregistering one plugin (
cacheprovider) also unregisters dependent plugins (stepwise).
test_blocked_plugin_can_be_used(self, pytestpm: PytestPluginManager) -> NoneTests that a plugin can be re-enabled after being previously disabled on the command line.
Important Implementation Details / Algorithms
Plugin Registration and Blocking:
The plugin manager keeps track of registered plugins and supports blocking plugins based on command-line options. Once a plugin is blocked, attempts to register it are ignored.Hook Specification Addition:
Plugins can add new hook specifications dynamically usingpytest_addhooks. The tests verify that hooks added this way are callable and integrate correctly.Conftest Importing:
The plugin manager importsconftest.pyfiles as plugins and manages their lifecycle carefully, including handling case sensitivity issues on Windows.Hook Tracing:
The plugin manager supports tracing hook calls, which is useful for debugging. The tracing code maintains indentation levels and ensures that exceptions during hooks do not corrupt the trace state.Environment Variable Plugins:
Plugins listed in thePYTEST_PLUGINSenvironment variable are imported automatically at startup, supporting dynamic plugin loading based on the environment.Command-Line Parsing for Plugins:
The manager parses-poptions to enable or disable plugins at startup, with safeguards to prevent disabling critical plugins like "main".
Interaction with Other Parts of the System
Pytesterfixture:
Used extensively to create isolated test environments, temporary plugin files, and simulate pytest runs.ConfigandSession:
Used to simulate pytest configuration and test session setup to validate plugin manager behavior in realistic contexts._pytest.config.PytestPluginManager:
The subject under test; this is the main class managing plugin registration, import, and hook invocation.Environment and Filesystem:
Several tests manipulate environment variables and filesystem layout (like creating conftest files) to verify plugin discovery and import behavior.pytesthooks and options:
The tests check that plugins correctly extend pytest's hook system and command-line options.
Visual Diagram: Class Diagram of Key Test Classes
classDiagram
class TestPytestPluginInteractions {
+test_addhooks_conftestplugin(pytester, _config_for_test)
+test_addhooks_nohooks(pytester)
+test_do_option_postinitialize(pytester)
+test_configure(pytester)
+test_conftestpath_case_sensitivity(pytester)
+test_hook_tracing(_config_for_test)
+test_hook_proxy(pytester)
+test_hook_with_addoption(pytester)
}
class TestPytestPluginManager {
+test_register_imported_modules()
+test_canonical_import(monkeypatch)
+test_consider_module(pytester, pytestpm)
+test_consider_module_import_module(pytester, _config_for_test)
+test_consider_env_fails_to_import(monkeypatch, pytestpm)
+test_plugin_skip(pytester, monkeypatch)
+test_consider_env_plugin_instantiation(pytester, monkeypatch, pytestpm)
+test_pluginmanager_ENV_startup(pytester, monkeypatch)
+test_import_plugin_importname(pytester, pytestpm)
+test_import_plugin_dotted_name(pytester, pytestpm)
+test_consider_conftest_deps(pytester, pytestpm)
}
class TestPytestPluginManagerBootstrapping {
+test_preparse_args(pytestpm)
+test_plugin_prevent_register(pytestpm)
+test_plugin_prevent_register_unregistered_already_registered(pytestpm)
+test_plugin_prevent_register_stepwise_on_cacheprovider_unregister(pytestpm)
+test_blocked_plugin_can_be_used(pytestpm)
}
TestPytestPluginInteractions --> Pytester
TestPytestPluginInteractions --> Config
TestPytestPluginManager --> Pytester
TestPytestPluginManager --> PytestPluginManager
TestPytestPluginManagerBootstrapping --> PytestPluginManager
Summary
`test_pluginmanager.py` is a critical test module that validates the behavior of `pytest`'s plugin management system. It covers plugin lifecycle events, hook specification and invocation, plugin importing (including error handling), environment-driven plugin loading, and command-line plugin control. By simulating a variety of plugin-related scenarios with dynamic plugin code generation and environment manipulation, it ensures robustness and correctness of `pytest`'s extensibility model.
This file interacts closely with `pytest` internals like `Config`, `Session`, and the plugin manager itself, and uses the `Pytester` helper extensively to create controlled test environments. The tests also confirm that user-facing features like options and markers integrate seamlessly when introduced by plugins.