writing_plugins.rst
Overview
This documentation file provides comprehensive guidance on writing, discovering, and managing plugins in the pytest testing framework. Plugins extend pytest's core functionality by implementing hook functions and fixtures for configuration, test collection, execution, reporting, and more.
The file covers:
How pytest discovers plugins (builtin, external, and local
conftest.pyplugins).Writing local and installable plugins.
Assertion rewriting and its importance for plugins.
Mechanisms to require plugins in tests or
conftest.py.Accessing plugins programmatically.
Registering custom markers used by plugins.
Testing plugins using the
pytesterplugin.
This file is primarily a conceptual and procedural guide rather than a source code file, but it includes essential code snippets and configuration examples.
Plugin Discovery and Loading Order
pytest loads plugins at startup in a defined sequence:
Blocking plugins specified with
-p no:nameon the command line.Loading all builtin plugins internal to pytest.
Loading plugins explicitly listed with
-p name.Loading third-party plugins registered via setuptools entry points unless disabled by
PYTEST_DISABLE_PLUGIN_AUTOLOAD.Loading plugins specified by the environment variable
PYTEST_PLUGINS.Loading all initial
conftest.pyfiles in test directories recursively (including loading plugins defined in theirpytest_pluginsvariables).
This order ensures precise control and predictability over plugin loading.
Conftest.py: Local Per-directory Plugins
Local plugins are implemented in `conftest.py` files within test directories. They provide directory-specific hook implementations. Hooks in `conftest.py` files apply only to tests in or below that directory.
Example:
# a/conftest.py
def pytest_runtest_setup(item):
print("setting up", item)
Running
pytest a/test_sub.pywill trigger the hook and print the message.Running
pytest test_flat.pywill not.
**Notes:**
Avoid importing from
conftest.pyfiles directly due to potential ambiguity.Some hooks cannot be implemented in non-initial
conftest.pyfiles because of the plugin discovery order.
Writing Your Own Plugin
Plugins implement hooks and/or fixtures to extend pytest. Examples include builtin plugins and external ones like the YAML plugin.
**Helpful resources:**
The
cookiecutter-pytest-plugintemplate provides a ready-to-use plugin scaffold with tests and packaging.
Contributing your plugin to pytest-dev is encouraged once it has users.
Making Your Plugin Installable by Others
To distribute a plugin, define an entry point in your packaging metadata under the `pytest11` group.
Example `pyproject.toml` snippet:
[project.entry-points.pytest11]
myproject = "myproject.pluginmodule"
pytest will load `myproject.pluginmodule` as a plugin.
**Important:**
Include the
"Framework :: Pytest"classifier to improve discoverability on PyPI.
Assertion Rewriting
pytest rewrites Python assert statements in test modules and plugins to provide detailed failure introspection.
Achieved via a PEP 302 import hook installed early.
Only test modules (matching
python_files) and plugin modules are rewritten.Plugins must be registered for assertion rewriting, especially if they contain helpers with asserts.
Example to register assertion rewriting for a helper module:
# pytest_foo/__init__.py
import pytest
pytest.register_assert_rewrite("pytest_foo.helper")
Requiring/Loading Plugins in Tests or Conftests
Use the global variable `pytest_plugins` to require plugins within test modules or `conftest.py` files:
pytest_plugins = ["name1", "name2"]
Supports recursive loading of plugins if those plugins also declare
pytest_plugins.Can also specify a single string if only one plugin is required.
Deprecated to require plugins via
pytest_pluginsin non-rootconftest.pyfiles because plugins affect the entire directory tree.
Plugins loaded via `pytest_plugins` are automatically registered for assertion rewriting unless already imported.
Accessing Another Plugin by Name
Plugins can collaborate by referencing one another through the plugin manager:
plugin = config.pluginmanager.get_plugin("name_of_plugin")
Use `pytest --trace-config` to view loaded plugins and their names.
Registering Custom Markers
Plugins that define custom markers should register them to avoid warnings and improve usability.
Example:
def pytest_configure(config):
config.addinivalue_line("markers", "cool_marker: this one is for cool tests.")
config.addinivalue_line("markers", "mark_with(arg, arg2): this marker takes arguments.")
Testing Plugins with the pytester Plugin
pytest includes a plugin called `pytester` that helps write tests for plugins.
By default,
pytesteris disabled.Enable by adding
pytest_plugins = ["pytester"]in your testingconftest.pyor via-p pytester.
Example plugin fixture:
import pytest
def pytest_addoption(parser):
group = parser.getgroup("helloworld")
group.addoption(
"--name",
action="store",
dest="name",
default="World",
help='Default "name" for hello().',
)
@pytest.fixture
def hello(request):
def _hello(name=None):
if not name:
name = request.config.getoption("name")
return f"Hello {name}!"
return _hello
Testing the plugin with `pytester`:
def test_hello(pytester):
pytester.makeconftest(
"""
import pytest
@pytest.fixture(params=["Brianna", "Andreas", "Floris"])
def name(request):
return request.param
"""
)
pytester.makepyfile(
"""
def test_hello_default(hello):
assert hello() == "Hello World!"
def test_hello_name(hello, name):
assert hello(name) == f"Hello {name}!"
"""
)
result = pytester.runpytest()
result.assert_outcomes(passed=4)
`pytester` supports copying example files and running tests in isolated environments.
Visual Diagram: Plugin Workflow and Structure
flowchart TD
A[pytest Startup] --> B{Plugin Discovery Order}
B -->|1. Blocked Plugins (-p no:name)| C[Skip Plugins]
B -->|2. Load Builtin Plugins| D[Builtin Plugins]
B -->|3. Load Plugins (-p name)| E[Explicit Plugins]
B -->|4. Load Entry Point Plugins| F[External Plugins]
B -->|5. Load Env Vars (PYTEST_PLUGINS)| G[Env Plugins]
B -->|6. Load conftest.py Files| H[Local Plugins (conftest.py)]
subgraph Plugin Types
D
E
F
G
H
end
Plugin Types --> I[Hook Function Calls]
I --> J[Test Collection, Setup, Run, Reporting]
J --> K[Assertion Rewriting]
J --> L[Fixtures and Markers]
J --> M[Testing Plugins (pytester)]
style A fill:#f9f,stroke:#333,stroke-width:2px
style B fill:#bbf,stroke:#333,stroke-width:2px
Summary
pytest plugins extend functionality by implementing hooks and fixtures.
Plugins are discovered and loaded in a specific order at pytest startup.
Local per-directory plugins reside in
conftest.pyfiles.Plugins can be packaged and distributed via setuptools entry points.
Assertion rewriting enhances assertion introspection and must be managed properly in plugins.
Plugins can require and interact with other plugins programmatically.
Custom markers should be registered to avoid warnings.
The
pytesterplugin facilitates testing plugin code in isolated environments.
This documentation provides both conceptual explanations and practical examples to help developers write, distribute, and test pytest plugins effectively.