What are Pytest Hooks?

Hooks are a key part of Pytest’s plugin system used to extend Pytest’s functionality.

At their core, Pytest Hooks are gateway points, allowing you to inject logic at specific stages of the test execution process to modify or extend the behaviour of tests based on test events.

Pytest offers a variety of hooks, each designed for different purposes and stages of the test cycle.

Types of Pytest Hooks

Pytest Hooks are categorized based on the stage of the testing process they’re involved in.

Bootstrapping Hooks

  • Called at the very beginning and end of the Pytest test run. Crucial for setting up and tearing down configurations or environments that are needed for the entire test suite.

  • Initialization Hooks

  • These hooks come into play after bootstrapping and are instrumental for tasks like adding command-line options or integrating new plugins. They set the stage for a customized testing environment.

  • Collection Hooks

  • These hooks deal with discovering and organizing test cases. They give you the power to influence how Pytest collects tests, allowing you to add, modify, or skip tests based on custom criteria.

  • Test running (runtest) hooks

  • These hooks offer a way to customize test execution. They’re incredibly versatile, enabling actions before, during, and after a test is run. This can range from setting up test data to cleaning up resources after a test.

Reporting Hooks

  • These hooks are all about how test results are processed and presented. They provide a means to customize the output, create custom reports, or even integrate with external systems for reporting purposes.

Debugging/Interaction Hooks

  • These hooks are invaluable for debugging. They come into play when tests fail or when you need to drop into an interactive session. They can help in inspecting the state of a test at various points or managing breakpoints..

Example - pytest_sessionstart and pytest_sessionfinish Hooks

Let’s look at how to use 2 simple hooks - pytest_sessionstart and pytest_sessionfinish - to perform some custom actions at the start and end of the test session.

In your conftest.py file, let’s define these hooks.

import pytest@pytest.hookimpl()def pytest_sessionstart(session):    print("Hello from \`pytest_sessionstart\` hook!")@pytest.hookimpl()def pytest_sessionfinish(session, exitstatus):    print("Hello from \`pytest_sessionfinish\` hook!")    print(f"Exit status: {exitstatus}")

You’ll notice that I’ve used the @pytest.hookimpl() decorator to mark these functions as hook implementations. This is a way of telling Pytest that these functions are implementations of hooks.

Let’s also define a simple test for the sake of this example.

./tests/example1/test_example1.py

def test_example1_pass():    print("Running test1")    assert True

Ordering of Pytest Hooks

Not only do Pytest Hooks allow us cool customization options, but also control the order in which these are executed.

You can do this via the @pytest.hookimpl decorator.

The @pytest.hookimpl decorator is used to mark a function as a hook implementation.

  • Execution as Early as Possible
    By using tryfirst=True as an argument, you can make a hook implementation execute earlier than others.

@pytest.hookimpl(tryfirst=True)

  • Execution as Late as Possible
    Alternatively, trylast=True ensures the hook implementation executes later in the sequence.

  • Hook Wrappers
    The hookwrapper=True argument creates a hook that wraps around all others. It can execute code both before and after the standard hooks.

Plugins vs Hooks

As you learnt, hooks are predefined points in the framework’s execution at which you can insert your own code to alter or enhance the testing process.

Plugins, on the other hand, are external or internal extensions that utilize these hooks to integrate additional features into Pytest.

plugins are a way to package and distribute custom hooks and fixtures, making them reusable across different projects

Fixtures vs 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.

While fixtures primarily focus on setting up and tearing down test conditions, hooks give you the leverage to customize the testing process itself.

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.

You can define hooks in any Python file and then register them using the pytest.hookimpl() decorator.

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.