Teams with large suites need custom behavior (opt-in slow tests, failure artifacts, ordering) that only hooks provide.
pytest is built from plugins communicating through hooks, implemented with the pluggy library. Any conftest.py is a local plugin: define a function with a hook's name (all start with pytest_) and pytest calls it at the right moment.
Frequently used hooks:
pytest_addoption(parser): add command-line flags and ini options, read later viaconfig.getoption().pytest_configure(config): one-time setup, e.g. registering markers withconfig.addinivalue_line("markers", ...).pytest_collection_modifyitems(config, items): reorder, deselect or mark collected tests. The classic recipe skipsslowtests unless--runslowis given.pytest_runtest_makereport(item, call): build or inspect the report for each phase (setup, call, teardown). As a hook wrapper (@pytest.hookimpl(wrapper=True)andreport = yield), you can attach the outcome to the item and save screenshots or logs on failure.pytest_sessionfinish(session, exitstatus): summarize after the run.
Hook functions only receive the arguments they name, so you can declare a subset. Results of firstresult hooks stop at the first non-None return.
When local hooks are useful across projects, package them as a plugin module with a pytest11 entry point. pytester, a built-in fixture (enable with pytest_plugins = ["pytester"]), runs pytest in a temp directory to test your plugin.
Example
# conftest.py
import pytest
def pytest_addoption(parser):
parser.addoption("--runslow", action="store_true", help="run slow tests")
def pytest_configure(config):
config.addinivalue_line("markers", "slow: mark test as slow")
def pytest_collection_modifyitems(config, items):
if config.getoption("--runslow"):
return
skip_slow = pytest.mark.skip(reason="need --runslow")
for item in items:
if "slow" in item.keywords:
item.add_marker(skip_slow)
@pytest.hookimpl(wrapper=True)
def pytest_runtest_makereport(item, call):
report = yield
if report.when == "call" and report.failed:
print(f"\nFAILED {item.nodeid}: save artifacts here")
return reportRemember
- conftest.py is a local plugin; define pytest_* hook functions
- pytest_addoption + config.getoption for custom flags
- pytest_collection_modifyitems to skip, mark or reorder tests
- Hook wrappers (wrapper=True, yield) see the result of other hooks
Watch out for
- Defining pytest_addoption in a nested conftest: options must be added in the rootdir conftest or a plugin loaded at startup.