Introduction To Pytest Hooks (A Practical Guide For Beginners)
Have you ever wondered how to customize the behaviour of your test suite in Pytest?
Perhaps you want to set a configfile path or read it, before executing your tests?
Maybe set some global variables for Dev or Prod or send Slack notifications after the test suite has finished running?
How do you achieve this in Pytest?
Well, you can do all this and more using Pytest Hooks.
Pytest hooks are intervention points within the Pytest framework, allowing you to plug in or alter the default behaviour of the testing process.
You can control Pytest’s behaviour at various stages of the test lifecycle, such as before test collection, after test execution, or when exceptions are raised.
They’re different from Pytest fixtures in the sense that they’re not directly called by tests but invoked automatically based on test events.
Hooks work alongside plugins to extend Pytest’s functionality.
Think of them as strategically placed entry points or “hooks” where you can insert your own code to add or alter functionality.
In this comprehensive tutorial, you’ll learn everything you need to know about using Pytest Hooks and how to use some of the most popular ones with examples.
So buckle up, and let’s begin!
What You’ll Learn
By the end of this article, you should be able to:
- grasp the basics of Pytest hooks and their uses.
- understand different types of Pytest hooks to judge which one to use when.
- learn about the ordering of Pytest hooks.
- gain insights into how hooks, plugins and fixtures are different and interlinked at the same time.
- implement hooks yourself.
What are Pytest Hooks?
Hooks are a key part of Pytest’s plugin system used to extend Pytest’s functionality.
Pytest offers a variety of hooks, each designed for different purposes and stages of the test cycle.
There are too many, but here are some of the most commonly used hooks:
Types of Pytest Hooks
Pytest Hooks are categorized based on the stage of the testing process they’re involved in.
Bootstrapping Hooks
Here are some examples of the most commonly used bootstrapping hooks, pulled straight from the documentation :
-
pytest_load_initial_conftests
- Called when initial conftest files are loaded. -
pytest_cmdline_parse
- Called after the command line has been parsed but before any further action is taken. -
pytest_cmdline_main
- Called after command line options have been parsed and all plugins and initial conftest files have been loaded.
Initialization Hooks
Following are some examples of the most commonly used initialization hooks.
-
pytest_addoption
- Called when command line options are parsed. It allows plugins to register new command line options or modify existing ones. Example Guide . -
pytest_configure
- This hook allows you to perform custom configuration of the test environment like pass in a config parameter or share global variables. -
pytest_unconfigure
- Called before test execution exits. It allows plugins to perform custom cleanup actions. -
pytest_addhooks
- Called when plugins are initialized. It allows plugins to register new hooks or modify existing ones. -
pytest_sessionstart
- Called after the pytest session object has been created and before performing collection and entering the run test loop. -
pytest_sessionfinish
- Called after the test session has finished. It allows plugins to perform custom actions after all tests have been run. -
pytest_plugin_registered
- Called when a plugin is registered. It allows plugins to perform custom actions when other plugins are registered.
Collection Hooks
Some popular collection hooks are:
-
pytest_collection
- Perform the collection phase for the given session. -
pytest_ignore_collect
- Allows plugins to ignore certain items during collection. -
pytest_collect_directory
- Allows plugins to modify the collection of directories. -
pytest_collect_file
- Allows plugins to modify the collection of files. -
pytest_generate_tests
- Allows plugins to dynamically generate tests or modify existing ones based on Pytest Parametrize. Here’s a more detailed guide onpytest_generate_tests
and how to use it to dynamically generate tests. -
pytest_make_parametrize_id
- Allows plugins to customize the test ids as part of Pytest Parametrization . -
pytest_collection_modifyitems
- It lets you modify the collected test items (test functions, classes, etc.) before the actual test run. You can use this hook to dynamically filter or reorder tests. -
pytest_collection_finish
- Allows plugins to perform custom actions after collection has finished.
Test Running (runtest) Hooks
Here are some of the popular test running hooks:
-
pytest_runtestloop
- Allows plugins to perform custom actions before the test loop starts. -
pytest_runtest_logstart
- Called at the start of running the runtest protocol for a test. -
pytest_runtest_logfinish
- Called at the end of running the runtest protocol for a test. -
pytest_runtest_makereport
- Called to create a test report for the given test item.
Reporting Hooks
-
pytest_make_collect_report
- Called to create a collection report for the given item. -
pytest_itemcollected
- Called after an item has been collected. -
pytest_collectreport
- Called to create a collection report for the given item. -
pytest_deselected
- Called when a test item has been deselected e.g. by keyword. -
pytest_report_collectionfinish
- Called after collection has finished. -
pytest_report_teststatus
- Called to report the status of a test. -
pytest_report_to_serializable
- Called to convert a report to a serializable format e.g. to JSON. -
pytest_terminal_summary
- Called at the end of the test session to print a summary of the test run. -
pytest_fixture_setup
- Called at the start of setting up a fixture. -
pytest_fixture_post_finalizer
- Called after fixture teardown but before the cache is cleared. -
pytest_warning_recorded
- Called when a warning is recorded. -
pytest_runtest_logreport
- Called to report the result of a test.
Debugging/Interaction Hooks
Here are some of the popular debugging/interaction hooks:
-
pytest_internalerror
- Called when a Pytest internal error occurs. -
pytest_keyboard_interrupt
- Called when a keyboard interrupt occurs. -
pytest_enter_pdb
- Called when entering the Python debugger.
Now let’s loook into how to actually use Pytest Hooks.
Project Set Up
Getting Started
The project has the following structure - a simple repo with a test file and
conftest.py
.
1 |
. |
Prerequisites
You don’t need to know everything but some basics of Python and Pytest would be helpful.:
To get started, clone the repo here and install any dependencies into your virtual environment via pip.
Example -
pytest_sessionstart
and
pytest_sessionfinish
Hooks
In your
conftest.py
file, let’s define these hooks.
1 |
import pytest |
Let’s also define a simple test for the sake of this example.
./tests/example1/test_example1.py
1 |
def test_example1_pass(): |
Running the tests will now show the output of the hooks.
Example -
pytest_runtest_makereport
Hook
Having looked at the session hooks, let’s take a look at how to use one of the reporting hooks.
1 |
|
- The goal of this hook is to print out the outcome and duration of each test after it has been run.
-
We’ve used the
@pytest.hookimpl(tryfirst=True)
decorator to ensure that this hook is executed before other hooks. -
The
pytest_runtest_makereport
hook takes 2 arguments -item
andcall
. Theitem
argument represents the test item, and thecall
argument represents the call information. -
We check if the
call
is for the “call” phase of the test, and if so, we access the test outcome, duration, and ID and print them out. -
The
call.excinfo
attribute is specifically an exception info object for failed tests.call.excinfo
beingNone
typically indicates the test passed or was skipped, but it’s primary purpose is to provide details on exceptions. -
We can get the test node id using
item.nodeid
.
These are just a couple of basic items but you can see how powerful these hooks can be.
Ordering of Pytest Hooks
You can do this via the
@pytest.hookimpl
decorator.
The
@pytest.hookimpl
decorator is used to mark a function as a hook implementation.
1 |
# Plugin 1 |
1 |
# Plugin 2 |
1 |
# Plugin 3 |
Now, let’s see the order in which the hooks from the above examples would execute:
Plugins vs Hooks
Check out our article on the 8 best Pytest plugins to supercharge your testing workflow.
In essence, plugins are a way to package and distribute custom hooks and fixtures, making them reusable across different projects .
Fixtures vs Hooks
Maybe you’re wondering, what about fixtures?
Don’t they do the same thing as some of the Pytest hooks?
Pytest Fixtures are reusable pieces of code that you can invoke in your tests to set up a specific state or environment before the test runs and optionally clean up after the test is done.
These important operations are commonly referred to as setup and teardown .
This could include preparing database connections, creating test data, or configuring system states.
Fixtures help in maintaining a clean, DRY (Don’t Repeat Yourself) codebase, making tests easier to write and understand.
While fixtures primarily focus on setting up and tearing down test conditions, hooks give you the leverage to customize the testing process itself.
Together, they help you tailor Pytest to your specific needs.
Functionality :
- Hooks are used to alter the framework’s behavior and react to various testing events.
- Fixtures are used for setting up and tearing down test environments and states.
Invocation :
- Hooks are invoked automatically by Pytest based on certain events in the test lifecycle.
-
Fixtures are invoked explicitly by naming them as parameters in tests or other fixtures, or by setting the
autouse=True
flag.
Scope and Impact :
- Hooks have a more global impact, influencing the overall behaviour of the test suite.
-
Fixtures have a localized impact, managing the environment for specific tests or groups of tests, controlled by the
scope
parameter.
FAQs
Do Pytest Hooks Have Any Significant Performance Implications on My Test Suite?
Pytest hooks generally do not have significant performance implications on your test suite.
Can Pytest Hooks be Defined Outside
conftest.py
?
Defining your hooks in
conftest.py
is indeed common practice.
However, you can absolutely define Pytest hooks outside of
conftest.py
.
But that’s a topic for another day.
Can I Use Pytest Hooks to Dynamically Generate Tests based on Certain Conditions?
The primary hook for this magic trick is
pytest_generate_tests
.
Lucky for you, we’ve already written a detailed guide on how to do this. Check it out here .
How Do I Access Command Line Arguments Passed to Pytest Within a Hook?
This article goes into detail on how to do this.
Can I Use Pytest Hooks to Share Global Variables Between Tests?
This article on Python Unit Testing Best Practices can help you design your suite in the best way from the get-go.
Session-scoped fixtures can also help with this.
How To Access All Collected Test Reports in Pytest?
This is a question asked by a fellow developer on StackOverflow who wanted to send logs over to Datadog.
Now this is a great example of the use case of reporting hooks, for example,
pytest_terminal_summary
.
This hook is invoked at the end of the test session and allows you to interact with the test reports that have been collected over the session.
It’s like getting a comprehensive summary of the entire test run, which you can then use to generate custom reports or perform analysis.
You can read more about how to use it here .
Conclusion
In this article we covered a lot on Pytest hooks.
If you have ideas for improvement or like me to cover anything specific, please send me a message via Twitter , GitHub or Email .
Till the next time… Cheers!
Additional Reading
Link to Example Code Repo
What Are Pytest Fixture Scopes? (How To Choose The Best Scope For Your Test)
Python Unit Testing Best Practices For Building Reliable Applications
How To Use Pytest With Command Line Options (Easy To Follow Guide)
What is Setup and Teardown in Pytest? (Importance of a Clean Test Environment)
How Pytest Fixtures Can Help You Write More Readable And Efficient Tests
8 Useful Pytest Plugins To Make Your Python Unit Tests Easier, Faster and Prettier
How to Effortlessly Generate Unit Test Cases with Pytest Parameterized Tests
A Beginner’s Guide To
pytest_generate_tests
(Explained With 2 Examples)
What Is The pytest_configure Hook? (A Simple Guide)
Official Docs - pytest Writing Hooks
Official Docs - pytest Hooks
Understanding Hooks in pytest