Skip to content

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:

bash
pip install pytest-timeout    # No configuration needed — effective immediately

You can verify this via the extended test header:

bash
$ pytest --trace-config
...
plugins: cov-7.x, xdist-3.x, timeout-2.x

Besides automatic entry-point loading there are two explicit channels:

  1. The pytest_plugins variable: declare plugin modules to load in the root conftest.py;
    python
    # content of conftest.py (root conftest only; non-root usage is deprecated)
    pytest_plugins = ("myapp.testsupport.myplugin",)
  2. The -p NAME command-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:

python
# 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")
python
# 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:

bash
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 allowlist

To make a disablement a project-wide convention, put it in the config file's addopts:

toml
# content of pyproject.toml
[tool.pytest.ini_options]
addopts = ["-p", "no:cacheprovider"]   # Permanently turn off the cache plugin in this project

Avoid 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.

Combined with later chapters of this course, here are the high-popularity plugins called out by the official documentation:

PluginOne-line positioningWhere in this course
pytest-covCoverage measurement, compatible with distributed runsChapter 13
pytest-xdistParallel execution across CPUs/hostsChapter 14
pytest-timeoutPer-marker or global timeouts to prevent hangsHands-on in this chapter
pytest-rerunfailuresAutomatic reruns to tame flaky testsChapter 20
pytest-instafailImmediate failure reporting without waiting for the full run—
pytest-django / pytest-bddFramework-level / BDD-level integration—

Use pytest-timeout to feel the "zero-config by default, tunable when needed" plugin experience:

python
# 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
toml
# 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:

python
# 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:

  1. Start with zero plugins; introduce one only when you hit a real pain point;
  2. Pin team-mandatory plugins and their minimum versions with required_plugins (Chapter 11);
  3. Keep CI and local runs on the same pyproject.toml; inject differences only via PYTEST_ADDOPTS.

12.5 Chapter Summary ​

  • Entry points (pytest11) make plugins work out of the box; the pytest_plugins variable and the -p option provide explicit loading channels;
  • --trace-config shows active plugins, -p no:NAME disables one for a single run, and --disable-plugin-autoload turns off autoloading (8.4+);
  • pytest-cov/xdist/timeout/rerunfailures are 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 ​

  1. Install pytest-timeout, tag a time.sleep(10) test with @pytest.mark.timeout(1), observe the failure output, then compare with -p no:timeout to see disabled behavior.
  2. Run pytest --trace-config, record every active plugin in your environment; then run twice with -p no:cacheprovider and check that .pytest_cache no longer updates.
  3. Declare a minimal plugin module of your own (even one empty hook) in the root conftest.py via pytest_plugins and 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.