Writing plugins

It is easy to implement local conftest plugins for your own project or pip-installable plugins that can be used throughout many projects, including third party projects. Please refer to How to install and use plugins if you only want to use but not write plugins.

A plugin contains one or multiple hook functions. Writing hooks explains the basics and details of how you can write a hook function yourself. pytest implements all aspects of configuration, collection, running and reporting by calling well specified hooks of the following plugins:

  • builtin plugins: loaded from pytest’s internal _pytest directory.

  • external plugins: installed third-party modules discovered through entry points in their packaging metadata

  • conftest.py plugins: modules auto-discovered in test directories

In principle, each hook call is a 1:N Python function call where N is the number of registered implementation functions for a given specification. All specifications and implementations follow the pytest_ prefix naming convention, making them easy to distinguish and find.

Plugin discovery order at tool startup

pytest loads plugin modules at tool startup in the following way:

  1. by scanning the command line for the -p no:name option and blocking that plugin from being loaded (even builtin plugins can be blocked this way). This happens before normal command-line parsing.

  2. by loading all builtin plugins.

  3. by scanning the command line for the -p name option and loading the specified plugin. This happens before normal command-line parsing.

  4. by loading all plugins registered through installed third-party package entry points, unless the PYTEST_DISABLE_PLUGIN_AUTOLOAD environment variable is set.

  5. by loading all plugins specified through the PYTEST_PLUGINS environment variable.

  6. by loading all “initial” conftest.py files:

    • determine the test paths: specified on the command line, otherwise in testpaths if defined and running from the rootdir, otherwise the current dir

    • for each test path, load conftest.py and test*/conftest.py relative to the directory part of the test path, if exist. Before a conftest.py file is loaded, load conftest.py files in all of its parent directories. After a conftest.py file is loaded, recursively load all plugins specified in its pytest_plugins variable if present.

conftest.py: local per-directory plugins

Local conftest.py plugins contain directory-specific hook implementations. Hook Session and test running activities will invoke all hooks defined in conftest.py files closer to the root of the filesystem. Example of implementing the pytest_runtest_setup hook so that is called for tests in the a sub directory but not for other directories:

a/conftest.py:
    def pytest_runtest_setup(item):
        # called for running each test in 'a' directory
        print("setting up", item)

a/test_sub.py:
    def test_sub():
        pass

test_flat.py:
    def test_flat():
        pass

Here is how you might run it:

pytest test_flat.py --capture=no  # will not show "setting up"
pytest a/test_sub.py --capture=no  # will show "setting up"

Note

If you have conftest.py files which do not reside in a python package directory (i.e. one containing an __init__.py) then “import conftest” can be ambiguous because there might be other conftest.py files as well on your PYTHONPATH or sys.path. It is thus good practice for projects to either put conftest.py under a package scope or to never import anything from a conftest.py file.

See also: pytest import mechanisms and sys.path/PYTHONPATH.

Note

Some hooks cannot be implemented in conftest.py files which are not initial due to how pytest discovers plugins during startup. See the documentation of each hook for details.

Writing your own plugin

If you want to write a plugin, there are many real-life examples you can copy from:

All of these plugins implement hooks and/or fixtures to extend and add functionality.

Note

Make sure to check out the excellent cookiecutter-pytest-plugin project, which is a cookiecutter template for authoring plugins.

The template provides an excellent starting point with a working plugin, tests running with tox, a comprehensive README file as well as a pre-configured entry-point.

Also consider contributing your plugin to pytest-dev once it has some happy users other than yourself.

Making your plugin installable by others

If you want to make your plugin externally available, you may define a so-called entry point for your distribution so that pytest finds your plugin module. Entry points are a feature that is provided by packaging tools.

pytest looks up the pytest11 entrypoint to discover its plugins, thus you can make your plugin available by defining it in your pyproject.toml file.

# sample ./pyproject.toml file
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "myproject"
classifiers = [
    "Framework :: Pytest",
]

[project.entry-points.pytest11]
myproject = "myproject.pluginmodule"

If a package is installed this way, pytest will load myproject.pluginmodule as a plugin which can define