Chapter 12 · The Plugin System and Essential Plugins
Chapter goals: understand how pytest plugins "work the moment they are installed" (the three loading channels: entry points, conftest.py, and
-p), learn how to troubleshoot and disable plugins, and survey the most worthwhile plugins for real-world test engineering.
12.1 Why Plugins Work Out of the Box
pytest's extensibility is built on the pluggy plugin framework. Any Python package that registers a pytest11 entry point in its packaging metadata is automatically discovered and loaded by pytest at startup after pip install:
pip install pytest-timeout # No configuration needed — effective immediatelyYou can verify this via the extended test header:
$ pytest --trace-config
...
plugins: cov-7.x, xdist-3.x, timeout-2.xBesides automatic entry-point loading there are two explicit channels:
- The
pytest_pluginsvariable: declare plugin modules to load in the rootconftest.py;python# content of conftest.py (root conftest only; non-root usage is deprecated) pytest_plugins = ("myapp.testsupport.myplugin",) - The
-p NAMEcommand-line option: load a plugin module or entry point by name at startup.
Channel 2 loads ordinary Python modules, which means you can maintain private plugins inside your own repository. A minimal local plugin looks like this:
# content of myapp/testsupport/mini_plugin.py
def pytest_addoption(parser):
# Register a custom command-line option
parser.addoption("--env", default="test", help="Target environment")
def pytest_configure(config):
# Read configuration at startup and tag it
config.option.env = config.getoption("--env")# content of conftest.py -- load the local plugin above with -p
pytest_plugins = ("myapp.testsupport.mini_plugin",)From then on, running pytest -p myapp.testsupport.mini_plugin --env=staging makes the entire suite aware of the target environment.
Watch out for name collisions
The name pytest_plugins is reserved by pytest — do not use it as a module name for your own plugin.
12.2 The Three Plugin Tiers and How to Disable Them
pytest divides plugins into three tiers: built-in plugins (shipped with the main package, such as cacheprovider), external plugins (pip packages discovered automatically via entry points), and local plugins (the conftest.py files in each directory). When troubleshooting environment issues, first identify "which tier provided which behavior".
Ways to disable a plugin:
pytest -p no:cacheprovider # Disable the built-in cache plugin for this run
pytest --trace-config # Look up names first
pytest --disable-plugin-autoload # 8.4+: turn off all autoloads
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 PYTEST_PLUGINS=cov,xdist pytest # Load only an allowlistTo make a disablement a project-wide convention, put it in the config file's addopts:
# content of pyproject.toml
[tool.pytest.ini_options]
addopts = ["-p", "no:cacheprovider"] # Permanently turn off the cache plugin in this projectAvoid double registration when controlling plugins explicitly
The official docs specifically warn: when using --disable-plugin-autoload, do not specify the same plugin through multiple mechanisms at once (e.g. both -p xdist and PYTEST_PLUGINS=xdist) — duplicate registration causes errors.
12.3 A Survey of Popular Plugins
Combined with later chapters of this course, here are the high-popularity plugins called out by the official documentation:
| Plugin | One-line positioning | Where in this course |
|---|---|---|
pytest-cov | Coverage measurement, compatible with distributed runs | Chapter 13 |
pytest-xdist | Parallel execution across CPUs/hosts | Chapter 14 |
pytest-timeout | Per-marker or global timeouts to prevent hangs | Hands-on in this chapter |
pytest-rerunfailures | Automatic reruns to tame flaky tests | Chapter 20 |
pytest-instafail | Immediate failure reporting without waiting for the full run | — |
pytest-django / pytest-bdd | Framework-level / BDD-level integration | — |
Use pytest-timeout to feel the "zero-config by default, tunable when needed" plugin experience:
# content of test_timeout.py
import time
import pytest
@pytest.mark.timeout(0.5) # Test level: fails if this test exceeds 0.5 seconds
def test_slow_query():
time.sleep(5)
assert True# content of pyproject.toml -- global default of 10 seconds to keep CI from hanging
[tool.pytest.ini_options]
addopts = ["--timeout=10"]The full plugin catalog can be searched on PyPI with the pytest- prefix; the official site also maintains a plugin-compatibility listing keyed to pytest/Python versions.
Another common scenario is auto-tagging slow tests — a few lines of hook code achieve "plugin-like" behavior, previewing Chapter 16 on custom plugins:
# content of conftest.py -- auto-mark tests from integration modules as slow during collection
import pytest
def pytest_collection_modifyitems(config, items):
for item in items:
if "slow" in item.keywords: # Skip explicitly marked tests
continue
if getattr(item.function, "__module__", "").endswith("integration"):
item.add_marker(pytest.mark.slow)12.4 Selection Advice: Less Is More
A common beginner mistake is installing a dozen plugins at once. Every plugin changes collection, execution, or reporting behavior — the more plugins you have, the harder it gets to debug "my pytest behaves differently from yours". Recommendations:
- Start with zero plugins; introduce one only when you hit a real pain point;
- Pin team-mandatory plugins and their minimum versions with
required_plugins(Chapter 11); - Keep CI and local runs on the same
pyproject.toml; inject differences only viaPYTEST_ADDOPTS.
12.5 Chapter Summary
- Entry points (
pytest11) make plugins work out of the box; thepytest_pluginsvariable and the-poption provide explicit loading channels; --trace-configshows active plugins,-p no:NAMEdisables one for a single run, and--disable-plugin-autoloadturns off autoloading (8.4+);pytest-cov/xdist/timeout/rerunfailuresare the four most common production plugins;- Fewer plugins beat more — lock dependencies down with
required_plugins.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. After pip install pytest-timeout, pytest loads it with zero configuration. Which mechanism is responsible?
2. Which command shows which plugins are active in the current run and where they come from?
3. What is the correct command to temporarily disable the built-in cache plugin for one run?
4. Which statement about the pytest_plugins variable is correct?
🛠️ Hands-on Practice
- Install
pytest-timeout, tag atime.sleep(10)test with@pytest.mark.timeout(1), observe the failure output, then compare with-p no:timeoutto see disabled behavior. - Run
pytest --trace-config, record every active plugin in your environment; then run twice with-p no:cacheproviderand check that.pytest_cacheno longer updates. - Declare a minimal plugin module of your own (even one empty hook) in the root
conftest.pyviapytest_pluginsand verify it gets loaded.
The next chapter goes deep into the heavyweight plugin pytest-cov: how to read coverage reports, what branch coverage means, and how to enforce quality gates in CI: Chapter 13.