pytest implements all aspects of configuration, collection, running and reporting by calling well specified hooks. Virtually any Python module can be registered as a plugin. It can implement any number of hook functions (usually two or three) which all have a pytest_ prefix, making hook functions easy to distinguish and find. There are three basic location types:
local conftest.py plugins contain directory-specific hook implementations. Session and test running activities will invoke all hooks defined in conftest.py files closer to the root of the filesystem. Example: Assume the following layout and content of files:
a/conftest.py:
def pytest_runtest_setup(item):
# called for running each test in 'a' directory
print ("setting up", item)
a/test_in_subdir.py:
def test_sub():
pass
test_flat.py:
def test_flat():
pass
Here is how you might run it:
py.test test_flat.py # will not show "setting up"
py.test a/test_sub.py # 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 practise for projects to either put conftest.py under a package scope or to never import anything from a conftest.py file.
Installing a plugin happens through any usual Python installation tool, for example:
pip install pytest-NAME
pip uninstall pytest-NAME
If a plugin is installed, pytest automatically finds and integrates it, there is no need to activate it. We have a page listing all 3rd party plugins and their status against the latest py.test version and here is a little annotated list for some popular plugins:
To see a complete list of all plugins with their latest testing status against different py.test and Python versions, please visit pytest-plugs.
You may also discover more plugins through a pytest- pypi.python.org search.
If you want to write a plugin, there are many real-life examples you can copy from:
All of these plugins implement the documented well specified hooks to extend and add functionality.
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 setuptools or Distribute. pytest looks up the pytest11 entrypoint to discover its plugins and you can thus make your plugin available by defining it in your setuptools/distribute-based setup-invocation:
# sample ./setup.py file
from setuptools import setup
setup(
name="myproject",
packages = ['myproject']
# the following makes a plugin available to pytest
entry_points = {
'pytest11': [
'name_of_plugin = myproject.pluginmodule',
]
},
)
If a package is installed this way, pytest will load myproject.pluginmodule as a plugin which can define well specified hooks.
pytest loads plugin modules at tool startup in the following way:
by loading all builtin plugins
by loading all plugins registered through setuptools entry points.
by pre-scanning the command line for the -p name option and loading the specified plugin before actual command line parsing.
by loading all conftest.py files as inferred by the command line invocation:
Note that pytest does not find conftest.py files in deeper nested sub directories at tool startup. It is usually a good idea to keep your conftest.py file in the top level test or project root directory.
by recursively loading all plugins specified by the pytest_plugins variable in conftest.py files
You can require plugins in a test module or a conftest file like this:
pytest_plugins = "name1", "name2",
When the test module or conftest plugin is loaded the specified plugins will be loaded as well. You can also use dotted path like this:
pytest_plugins = "myapp.testsupport.myplugin"
which will import the specified module as a pytest plugin.
If a plugin wants to collaborate with code from another plugin it can obtain a reference through the plugin manager like this:
plugin = config.pluginmanager.getplugin("name_of_plugin")
If you want to look at the names of existing plugins, use the --traceconfig option.
If you want to find out which plugins are active in your environment you can type:
py.test --traceconfig
and will get an extended test header which shows activated plugins and their names. It will also print local plugins aka conftest.py files when they are loaded.
You can prevent plugins from loading or unregister them:
py.test -p no:NAME
This means that any subsequent try to activate/load the named plugin will it already existing. See Finding out which plugins are active for how to obtain the name of a plugin.
You can find the source code for the following plugins in the pytest repository.
_pytest.assertion | |
_pytest.capture | |
_pytest.config | |
_pytest.doctest | |
_pytest.genscript | |
_pytest.helpconfig | |
_pytest.junitxml | |
_pytest.mark | |
_pytest.monkeypatch | |
_pytest.nose | |
_pytest.pastebin | |
_pytest.pdb | |
_pytest.pytester | |
_pytest.python | |
_pytest.recwarn | |
_pytest.resultlog | |
_pytest.runner | |
_pytest.main | |
_pytest.skipping | |
_pytest.terminal | |
_pytest.tmpdir | |
_pytest.unittest |
pytest calls hook functions to implement initialization, running, test execution and reporting. When pytest loads a plugin it validates that each hook function conforms to its respective hook specification. Each hook function name and its argument names need to match a hook specification. However, a hook function may accept fewer parameters by simply not specifying them. If you mistype argument names or the hook name itself you get an error showing the available arguments.
All runtest related hooks receive a pytest.Item object.
For deeper understanding you may look at the default implementation of these hooks in _pytest.runner and maybe also in _pytest.pdb which interacts with _pytest.capture and its input/output capturing in order to immediately drop into interactive debugging when a test failure occurs.
The _pytest.terminal reported specifically uses the reporting hook to print information about a test run.
pytest calls the following hooks for collecting files and directories:
For influencing the collection of objects in Python modules you can use the following hook:
After collection is complete, you can modify the order of items, delete or otherwise amend the test items:
Session related reporting hooks:
And here is the central hook for reporting about test execution:
There are few hooks which can be used for special reporting or interaction with exceptions: