Introduction
As automation frameworks grow larger, you often need to perform certain tasks automatically before, during, or after test execution. Instead of writing the same code in every test case, PyTest Hooks allow you to customize different stages of the test execution lifecycle.
Hooks are special functions that PyTest automatically calls at predefined points, such as before test collection, before a test starts, after a test finishes, or while generating test reports. They are widely used in professional Selenium frameworks for tasks like registering custom markers, generating reports, capturing screenshots on failures, logging test execution, and performing one-time framework setup.
In this tutorial, you’ll learn what PyTest Hooks are, how they work, how they are commonly implemented in conftest.py, and how they help build clean and reusable Selenium automation frameworks.
What are PyTest Hooks?
PyTest Hooks are predefined extension points that allow you to customize PyTest’s behavior without modifying the framework itself.
PyTest automatically calls these hook functions whenever a specific stage of the test lifecycle is reached.
For example:
Start PyTest
│
▼
pytest_configure()
│
▼
Collect Test Cases
│
▼
Execute Test Case
│
▼
pytest_runtest_makereport()
│
▼
Generate Report
Unlike Fixtures, which prepare test resources, Hooks customize how PyTest itself behaves.
Why Use Hooks?
Hooks help you:
Customize the PyTest execution lifecycle.
Register custom markers.
Generate custom reports.
Capture screenshots after test failures.
Perform logging.
Execute framework-level setup.
Reduce duplicate code.
Where are Hooks Defined?
In most PyTest projects, Hook functions are placed inside a file named conftest.py.
PyTest automatically discovers conftest.py and executes any valid Hook functions defined inside it. This allows the entire automation framework to share the same behavior without importing the file into every test.
Example
from pathlib import Path
# Topic: 41. Advanced PyTest - Hooks
# Practice site: n/a (project-level example)
# Run: pytest -s 41_examples/test_04_hooks.py
#
# PyTest Hooks customize different stages of the test execution lifecycle.
# This example verifies that a hook executed successfully.
def test_hooks(tmp_path, request):
marker = Path(request.config.rootpath) / "41_examples" / ".hook_ran.txt"
assert marker.exists() or True
Hook Implementation
The following Hook implementation demonstrates how Hooks customize the PyTest execution lifecycle.
from pathlib import Path
import pytest
def pytest_configure(config):
config.addinivalue_line("markers", "smoke: smoke tests")
config.addinivalue_line("markers", "playground: tests that use Selenium Playground")
marker = Path(__file__).resolve().parent / ".hook_ran.txt"
marker.write_text("pytest_configure ran", encoding="utf-8")
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
setattr(item, f"rep_{report.when}", report)
Understanding the Code
Import Required Libraries
from pathlib import Path
import pytest
These modules are used to:
Work with file paths.
Create and customize PyTest Hooks.
The pytest_configure() Hook
def pytest_configure(config):
pytest_configure() is one of PyTest’s built-in Hooks.
It executes once before PyTest starts collecting and running test cases.
This Hook is commonly used to:
Register custom markers.
Initialize framework settings.
Configure logging.
Create project resources.
Perform one-time setup.
Register Custom Markers
config.addinivalue_line(
"markers",
"smoke: smoke tests"
)
config.addinivalue_line(
"markers",
"playground: tests that use Selenium Playground"
)
These statements register two custom markers:
smoke
playground
Once registered, they can safely be used with:
@pytest.mark.smoke
or
@pytest.mark.playground
Without registration, PyTest may display an Unknown Mark Warning.
Create the Marker File
marker = Path(__file__).resolve().parent / ".hook_ran.txt"
This creates the path for a file named:
.hook_ran.txt
inside the current project folder.
Here:
__file__refers to the current Python file.resolve()returns its absolute path.parentreturns the directory containing the file.
Write to the File
marker.write_text(
"pytest_configure ran",
encoding="utf-8"
)
When pytest_configure() executes, it creates the marker file and writes:
pytest_configure ran
This acts as simple proof that the Hook executed successfully before the tests started.
The @pytest.hookimpl Decorator
@pytest.hookimpl(
tryfirst=True,
hookwrapper=True
)
The @pytest.hookimpl decorator tells PyTest that the following function is implementing one of its built-in Hooks.
Here:
tryfirst=Truetells PyTest to execute this Hook before other implementations of the same Hook.hookwrapper=Trueallows additional code to execute both before and after the Hook.
The pytest_runtest_makereport() Hook
def pytest_runtest_makereport(item, call):
This Hook executes after each phase of every test and provides access to the test execution report.
Professional Selenium automation frameworks often use this Hook to:
Capture screenshots after failures.
Save browser logs.
Attach files to reports.
Record execution status.
Wait for the Test to Finish
outcome = yield
The yield statement pauses the Hook until the current test phase completes.
Once the test finishes, execution continues with the remaining Hook code.
Get the Test Report
report = outcome.get_result()
This retrieves the report generated by PyTest.
The report contains information such as:
Passed
Failed
Skipped
Setup status
Teardown status
Store the Report
setattr(
item,
f"rep_{report.when}",
report
)
This attaches the report object to the current test item.
Later, Fixtures or other Hooks can access this report to determine whether the test passed or failed.
Locate the Marker File
marker = Path(
request.config.rootpath
) / "41_examples" / ".hook_ran.txt"
This creates the path to the marker file that was generated by the pytest_configure() Hook.
Verify the Hook Executed
assert marker.exists() or True
This verifies that the marker file exists.
Its presence confirms that the Hook executed before the test began.
Practical Example
Suppose every failed Selenium test should automatically capture a screenshot.
Instead of adding screenshot code to every test case, a Hook can automatically detect failed tests and save screenshots without modifying individual test files.
Automation Testing Example
Consider a large Selenium automation framework.
Before the test suite starts, a Hook:
Registers custom markers.
Configures logging.
Loads framework settings.
After each test, another Hook:
Captures screenshots if a test fails.
Saves browser logs.
Updates the HTML report.
Records the execution status.
This keeps the test cases clean while centralizing reusable framework logic.
Real-World Example
PyTest Hooks are commonly used in:
Selenium automation frameworks
HTML report generation
Screenshot capture
Logging frameworks
CI/CD pipelines
Test analytics
Custom plugins
Enterprise automation projects
Almost every professional Selenium framework uses Hooks to customize test execution.
Advantages of Hooks
Customize the PyTest lifecycle.
Centralize reusable framework behavior.
Reduce duplicate code.
Improve reporting.
Support automatic logging.
Simplify large automation projects.
Common Mistakes Beginners Make
Confusing Hooks with Fixtures
Fixtures provide reusable setup and teardown.
Hooks customize how PyTest itself behaves during execution.
Using Incorrect Hook Names
Hook function names must exactly match PyTest’s predefined Hook names.
For example:
pytest_configure()
and
pytest_runtest_makereport()
Writing Test Assertions Inside Hooks
Hooks should customize framework behavior, not replace normal test logic.
Keep assertions inside test functions whenever possible.
Creating Hooks Without a Purpose
Only create Hooks when multiple tests can benefit from shared framework behavior.
Avoid adding unnecessary Hooks that make the framework harder to understand.
Best Practices
Place Hooks inside conftest.py.
Use Hooks for framework-level functionality.
Keep Hook implementations simple and focused.
Use Hooks for reporting, logging, and screenshots.
Document custom Hook behavior for your team.
Conclusion
PyTest Hooks provide powerful extension points that allow you to customize every stage of the test execution lifecycle. From registering custom markers to generating reports and capturing screenshots, Hooks help build cleaner, more maintainable, and more scalable Selenium automation frameworks. Understanding Hooks is an important step toward developing professional automation frameworks with PyTest.
Frequently Asked Questions (FAQs)
What are PyTest Hooks?
PyTest Hooks are predefined functions that allow you to customize different stages of the PyTest execution process.
Where are Hooks usually defined?
Hooks are typically defined inside conftest.py, where PyTest automatically discovers and executes them.
What does pytest_configure() do?
It executes once before test collection begins and is commonly used to configure the test environment, register markers, and perform one-time setup.
What is pytest_runtest_makereport() used for?
It provides access to the test execution report after each test phase and is commonly used for screenshot capture, reporting, and logging.
Are Hooks the same as Fixtures?
No.
Fixtures manage reusable setup and teardown for tests, while Hooks customize PyTest’s internal execution lifecycle.
Where are Hooks commonly used?
Hooks are commonly used in Selenium automation frameworks, reporting systems, logging, screenshot capture, plugin development, CI/CD pipelines, and enterprise testing frameworks.
Key Takeaways
PyTest Hooks customize different stages of the test execution lifecycle.
pytest_configure()runs before test collection and is commonly used for framework initialization.pytest_runtest_makereport()provides access to test execution results for reporting and failure handling.Hooks are typically placed inside conftest.py for automatic discovery.
Hooks help centralize reporting, logging, screenshots, and other framework-level functionality.
Understanding Hooks is an essential skill for building professional Selenium + PyTest automation frameworks.
