What Is The pytest_configure Hook? (A Simple Guide)

Say you’re working on a large test suite with a bunch of tests, each with its own set of requirements and dependencies, across different environments.

You want to run initialization commands in different environments, set up global variables, maybe slack notifications or ping an external API via a webhook to say your tests have begun.

How do you do this in Pytest? How do you tune Pytest to automatically execute tasks before running your tests?

After all you don’t want to write another script to do this. That’s just more code to maintain and more things to go wrong.

The answer lies in Pytest hooks. Hooks are gateways to customize and extend Pytest’s default behavior.

Think of them as strategically placed entry points or “hooks” within the Pytest execution cycle where you can insert your own code to add or alter functionality.

pytest_configure is a useful initialization hook that’s called after the command-line options are parsed, allowing you to configure the test environment before tests are executed.

In this comprehensive tutorial, you’ll learn about the pytest_configure hook and how to configure it to fit your testing needs.

We’ll explore its purpose, how to wield it effectively and witness its transformative impact on your Python testing strategy.

So buckle up, and let’s begin!

Link To Example Repo

What You’ll Learn

By the end of this tutorial, you’ll be able to:

  • grasp the concept and purpose of hooks in Pytest.
  • dive deep into the functionalities and applications of the pytest_configure hook.
  • use pytest_configure to - access shared global variables, command line options and define markers within your test suite.
  • gain insights into best practices for using pytest_configure.

Understanding Pytest Hooks

At their core, Pytest hooks are gateway points, allowing you to inject logic at specific stages of the test process to modify or extend the behaviour.

It’s like having the power to pause the execution of your tests, do some wizardry, and then resume, with everything now aligned just the way you want it.

Pytest offers a variety of hooks, each designed for different stages of the test cycle, like bootstrap hooks, initialization hooks and so on.

pytest_configure sets the stage before your tests run.

It’s your code’s first interaction with the testing environment, where you can add markers, configure plugins, or set command line global options.

Hooks also integrate natively with Pytest Fixtures and Parameters, allowing you to create a testing workflow that’s unique to your project.

Curious? Let’s dive deeper into the pytest_configure hook.

Project Set Up

Getting Started

Our project has the following structure - a simple repo with a few test and configuration files.

Hooks are generally defined in conftest.py files, which are automatically discovered by Pytest.

If you’re not familiar with conftest.py files, check out this guide.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.
├── .gitignore
├── README.md
├── requirements.txt
└── tests
├── conftest.py
├── example1
│ ├── conftest.py
│ └── test_pytest_configure_markers.py
├── example2
│ ├── conftest.py
│ └── test_pytest_configure_global_var.py
├── example3
│ ├── conftest.py
│ └── test_pytest_configure_cmd.py
├── input.txt
├── password_manager
│ ├── conftest.py
│ └── test_pytest_configure_password.py
└── test_pytest_configure.py

Prerequisites

To achieve the above objectives, knowledge of the following is recommended:

  • Python (3.10+)
  • Pytest

To get started, clone the repo here and install any dependencies via pip or your favourite package manager.

Using pytest_configure

The pytest_configure hook takes a parameter config, a pytest.Config object representing the configuration information.

Its a central point useful for configuring the Pytest environment before the test execution begins.

pytest_configure doesn’t return anything, but you can use the config object to access and modify the Pytest configuration.

Define Custom Markers with pytest_configure

Custom markers allow you to tag tests with specific labels (slow, fast, API, UI, etc.), which is useful for categorizing and selectively running tests in a large test suite with hundreds of tests.

To speed up your large test suite you can check out this post with 13 proven ways to improve the runtime of your test suite.

So how and where do you define these markers? (you can use them directly in your test functions, but without defining them, you’ll get warnings from Pytest).

Well, there are a few ways - you can define them in your pytest.ini file for one.

Another interesting way is using the pytest_configure hook defined in conftest.py file.

tests/example1/conftest.py

1
2
3
def pytest_configure(config):
config.addinivalue_line("markers", "ui: mark test as a UI test")
config.addinivalue_line("markers", "api: mark test as an API test")

tests/example1/test_pytest_configure_markers.py

1
2
3
4
5
6
7
8
9
10
11
12
import pytest

@pytest.mark.ui # Define the marker for the test
def test_ui_component():
# code for testing UI component
print("Testing UI component")

@pytest.mark.api # Define the marker for the test
def test_api_call():
# code for testing API call
print("Testing API call")

Here, test_ui_component is marked as a UI test, and test_api_call as an API test.

To run only the UI tests, you can use the -m flag and specify the marker name.

1
pytest -m ui -v

pytest configure custom markers

Similarly, you can run only the API tests by specifying the marker name.

1
pytest -m api -v

pytest configure custom markers

Share Global Variables with pytest_configure

Sometimes, you might want to define global variables that are accessible across your test suite. These could be config like settings, environment variables, or even a database connection.

pytest_configure allows you to add these variables to the config object.

Here’s an example:

tests/example2/conftest.py

1
2
def pytest_configure(config):
config.my_global_data = "Shared Value"

In this snippet, we set a global variable my_global_data with the value Shared Value.

This variable is no longer confined to a single test or module; it has been given a global stage, accessible through the config object.

Accessing the global variable is easy. You can use the request fixture to access the config object.

tests/example2/test_pytest_configure_global_var.py

1
2
3
4
5
6
7
import pytest

def test_example(request):
# Retrieve the global variable from the pytest configuration
global_value = request.config.my_global_variable

print(f"The global variable is: {global_value}")

pytest configure global variable

Accessing Command Line Options via pytest_addoption

To truly harness the power of pytest_configure, you can pair it with the pytest_addoption hook.

The pytest_addoption hook allows you to add custom command-line options to pytest.

This duo works like magic, allowing you to define custom command-line options and then use them within your hook.

I’ll show a small example to explain:

tests/example3/conftest.py

1
2
3
4
5
def pytest_addoption(parser):
parser.addoption("--custom-option", action="store", default="default")

def pytest_configure(config):
config.custom_option = config.getoption("--custom-option")

The parser.addoption method defines a new command-line option. To learn more about how command line options can be passed and accessed in Pytest, check out this guide.

In the pytest_configure function, we have passed the command-line argument --custom-option myValue.

tests/example3/test_pytest_configure_cmd.py

1
2
3
4
import pytest

def test_custom_option(pytestconfig):
assert pytestconfig.custom_option == "myValue" # or any other value passed via command line

The custom option can be easily accessed via the pytestconfig fixture.

1
pytest --custom-option=myValue

pytest configure cmd

Pretty neat, right?

With this basic understanding, let’s dive into detail how to implement pytest_configure in your projects.

Define Centralized Config via the pytest_configure hook

As you’ve seen above, pytest_configure is a powerful hook that allows you to configure the Pytest environment before the test execution begins.

A very interesting use case is to define central configuration settings for your test suite.

Let’s say we want to read a config file into an object and pass it to all our tests.

We can use pytest_configure to read the file and store its contents in the config object.

The content can then be extended to all tests via fixtures.

tests/conftest.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pytest
import os

def pytest_configure(config):
# Get the path to the text file in the same directory as conftest.py
current_directory = os.path.dirname(os.path.abspath(__file__))
input_file_path = os.path.join(current_directory, "input.txt")

# Read the content of the file and store it in the pytest config object
with open(input_file_path, "r") as file:
file_content = file.read()
config._input_file_content = file_content # Store the file content in the pytest config object

@pytest.fixture
def input_file_content(request):
return request.config._input_file_content

The above code snippet includes the read file settings defined in the pytest_configure hook.

It also defines a fixture input_file_content that returns the content of the file we read in pytest_configure.

tests/test_pytest_configure.py

1
2
3
4
def test_display_input(input_file_content):
print("Content from input file:")
print(input_file_content)
assert "Pytest with Eric" in input_file_content

The output will look like this:

pytest configure file handling

pytest_configure - A Replacement For Fixtures?

Now, you might think that why should you use the pytest_configure hook and why not fixtures themselves?

The answer is simple.

Hooks are NOT a replacement for fixtures. Rather a complement to them.

pytest_configure: Best for global, session-wide settings or variables that remain constant. Useful for setting up global variables, custom markers, custom configuration, and so on.

Fixtures: Best for setting up and tearing down resources needed for specific tests, with great flexibility in scope and reusability. Useful for database connections, API clients and so on.

Practical Example - pytest_configure

Let’s look at another simple use case for the pytest_configure hook.

After all, in this website we go beyond the basics and look at real world examples.

You’re developing a password manager application and you have some password validation rules in place. Annoying! I know, but it’s for the greater good.

When a user enters a password, there’s an auto-check that ensures the entered password meets your password criteria.

Besides that, you also want to mark your tests with a password marker, so you can run them separately from the rest of your tests.

To use the pytest_configure hook, you can define the password requirements in the conftest.py file.

tests/password_manager/conftest.py

1
2
3
4
5
6
7
8
9
10
import re
import pytest

def pytest_configure(config):
config.addinivalue_line(
"markers",
"password: mark a test to check password formatting"
)

config.password_pattern = re.compile(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&-])[A-Za-z\d@$!%*?&-]{8,}$')

As you can see, this conftest.py defines a regular expression to check if a password has at least one lowercase letter, one uppercase letter, one digit, and one special character. The password length is also set to a minimum of 8 characters.

Now, let’s put this to the test!

tests/password_manager/test_pytest_configure_password.py

1
2
3
4
5
6
7
import pytest

@pytest.mark.password
def test_password_format(pytestconfig):
password = input("Enter a password for testing: ")
assert pytestconfig.password_pattern.match(password), "Password does not meet the required format"

In the test file, we have a test function, test_password_format, that prompts the user to enter a password and then checks if it matches the pattern defined in pytest_configure.

pytest configure password

Viola! The password matches our set criteria.

Let’s also look at a failing case.

pytest configure wrong password

Albeit a simple example, it shows how you can use pytest_configure to define global variables and markers to smoothly use in your tests.

Other Useful Pytest Hooks

While pytest_configure is a powerful and versatile tool in your arsenal, it’s just the beginning.

Beyond pytest_configure, there are numerous hooks, each with its specific purpose and versatility:

  • pytest_addoption: This hook is like a custom command center, allowing you to add your own command-line options to Pytest.

  • pytest_fixture_setup: This hook lets you set up and tear down conditions before and after your fixtures are called, providing a clean and controlled testing canvas.

  • pytest_generate_tests: This hook is a master of variation, enabling parameterization of tests at runtime.

You can read more about the different hooks in the official Pytest documentation.

Conclusion

That’s all I have for you in this one!

I like to call pytest_configure and Pytest hooks - your Swiss Army knife. And in this article, you have learned all the reasons why that is so true.

You delved deep into Pytest hooks and pytest_configure, appreciating its versatility in setting up global variables and environment-specific settings.

You learnt different ways to use pytest_configure to define custom markers, add command-line options, custom config and so much more.

Lastly, you also compared fixtures with hooks and learned how to use them in tandem to create a testing workflow that’s unique to your project.

From now on, I enocurage you to be curious and experiment with different hooks and configurations to see what works best for your projects.

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 Learning

Link to Example Code Repo
Official Docs - Pytest Configuration
Official Docs - Pytest Hooks
13 Proven Ways To Improve Test Runtime With Pytest
A Beginner’s Guide To pytest_generate_tests (Explained With 2 Examples)
How To Use Pytest With Command Line Options (Easy To Follow Guide)
How Pytest Fixtures Can Help You Write More Readable And Efficient Tests
How to use pytest hook in global scope
How To Run A Single Test In Pytest (Using CLI And Markers)
What is Setup and Teardown in Pytest? (Importance of a Clean Test Environment)
Global Variable in conftest.py file
Ultimate Guide To Pytest Markers And Good Test Management