Configuration

You can use configuration files to change the way pytest runs. If you repeatedly use certain options in your tests, such as --verbose or --strict-markers, you can store them in a configuration file so that you don’t have to enter them again and again. In addition to the configuration files, there are a handful of other files that are helpful when using pytest to make writing and running tests easier:

pytest.ini

This is the most important configuration file of pytest, with which you can change the default behaviour of pytest. It also defines the root directory of pytest, or rootdir.

conftest.py

This file contains Test fixtures and hook functions. It can exist in rootdir or in any subdirectory.

__init__.py

If this file is stored in test subdirectories, it enables the use of identical test file names in several test directories.

If you already have a tox.ini, pyproject.toml or setup.cfg in your project, they can take the place of the pytest.ini file: tox.ini is used by tox, pyproject.toml and setup.cfg are used for packaging Python projects and can be used to store settings for various tools, including pytest.

You should have a configuration file, either pytest.ini, or a pytest section in tox.ini, pyproject.toml or in setup.cfg.

The configuration file defines the top-level directory from which pytest is started.

Let’s take a look at some of these files in the context of a project directory structure:

 items
 ├── …
 ├── pytest.ini
 ├── src
 │   └── …
 └── tests
     ├── __init__.py
     ├── conftest.py
     └── test_….py

In the case of the items project that we have used for testing so far, there is a pytest.ini file and a tests directory at the top level. We will refer to this structure when we talk about the various files in the rest of this section.

Saving settings and options in pytest.ini

[pytest]
addopts =
    --strict-markers
    --strict-config
    -ra
testpaths = tests
markers =
    smoke: Small subset of all tests
    exception: Only run expected exceptions

[pytest] marks the start of the pytest section. This is followed by the individual settings. For configuration settings that allow more than one value, the values can be written either in one or more lines in the form SETTING = VALUE1 VALUE2. With markers, however, only one marker per line is permitted.

This example is a simple pytest.ini file that I use in almost all my projects. Let’s briefly go through the individual lines:

addopts =

allows you to specify the pytest options that we always want to execute in this project.

--strict-markers

instructs pytest to issue an error instead of a warning for every unregistered marker that appears in the test code. This allows us to avoid typos in marker names.

--strict-config

instructs pytest to issue an error instead of a warning if difficulties arise when parsing configuration files. This prevents typing errors in the configuration file from going unnoticed.

-ra

instructs pytest to display not only additional information on failures and errors at the end of a test run, but also a test summary.

-r

displays additional information on the test summary.

a

displays all but the passed tests. This adds the information skipped, xfailed or xpassed to the failures and errors.

testpaths = tests

tells pytest where to look for tests if you have not specified a file or directory name on the command line. In our case, pytest searches in the tests directory.

At first glance, it may seem superfluous to set testpaths to tests, as pytest searches there anyway and we do not have any test_ files in our src or docs directories. However, specifying a testpaths directory can save a little startup time, especially if our src, docs or other directories are quite large.

markers =

is used to declare markers, as described in Selection of tests with your own markers.

See also

You can specify many other configuration settings and command line options in the configuration files, which you can display using the pytest --help command.

Using other configuration files

If you are writing tests for a project that already has a pyproject.toml, tox.ini or setup.cfg file, you can use pytest.ini to store your pytest configuration settings, or you can store your configuration settings in one of these alternative configuration files. The syntax of the two non-ini files is slightly different, so we will take a closer look at both files.

pyproject.toml

The pyproject.toml file was originally intended for the packaging of Python projects; however, it can also be used to define project settings.

As TOML is a different standard for configuration files than .ini files, the format is also slightly different:

[tool.pytest.ini_options]
addopts = [
    "--strict-markers",
    "--strict-config",
    "-ra"
    ]
testpaths = "tests"
markers = [
    "exception: Only run expected exceptions",
    "finish: Only run finish tests",
    "smoke: Small subset of all tests",
    "num_items: Number of items to be pre-filled for the items_db fixture"
    ]

Instead of [pytest], the section begins with [tool.pytest.ini_options], the values must be enclosed in quotes and lists of values must be lists of character strings in square brackets.

setup.cfg

The file format of the setup.cfg corresponds to an .ini file:

[tool:pytest]
addopts =
    --strict-markers
    --strict-config
    -ra
testpaths = tests
markers =
    smoke: Small subset of all tests
    exception: Only run expected exceptions

The only difference between this and pytest.ini is the specification of the [tool:pytest] section.

Warning

However, the parser of the .cfg file differs from the parser of the .ini file, and this difference can cause problems that are difficult to track down, see also pytest documentation.

Set rootdir

Before pytest searches for test files to execute, it reads the configuration file pytest.ini, tox.ini, pyproject.toml or setup.cfg, which contains a pytest section:

  • if you have specified a test directory, pytest will start searching there

  • if you have specified several files or directories, pytest starts with the parent directory

  • if you do not specify a file or directory, pytest starts in the current directory.

If pytest finds a configuration file in the start directory, this is the root and if not, pytest goes up the directory tree until it finds a configuration file that contains a pytest section. Once pytest has found a configuration file, it marks the directory in which it found it as rootdir. This root directory is also the relative root of the IDs. pytest also tells you where it has found a configuration file. Using these rules, we can run tests at different levels and be sure that pytest finds the correct configuration file:

 $ cd items
 $ pytest
 ============================= test session starts ==============================
 ...
 rootdir: /Users/veit/cusy/prj/items
 configfile: pyproject.toml
 testpaths: tests
 plugins: Faker-19.11.0
 collected 39 items
 ...

conftest.py for sharing local fixtures and hook functions

The conftest.py file is used to store fixtures and hook functions, see also Test fixtures and Plugins. You can have as many conftest.py files in a project as you like. Everything that is defined in a conftest.py file applies to tests in this directory and all subdirectories. If you have a conftest.py file at the top test level, the fixtures defined there can be used for all tests. If there are special fixtures that only apply to a subdirectory, these can be defined in another conftest.py file in this subdirectory. For example, the CLI tests may require different fixtures than the API tests, and you can also share some of them.

Tip

However, it is a good idea to keep only one conftest.py file so that you can easily find the fixture definitions. Even though we can always find out where a fixture is defined with pytest --fixtures -v, it is still easier if it is always defined in the one conftest.py file.

__init__.py to avoid collision of test file names

The __init__.py file allows you to have duplicate test filenames. If you have __init__.py files in each test subdirectory, you can use the same test filename in multiple directories, for example:

 items
 ├── …
 ├── pytest.ini
 ├── src
 │   └── …
 └── tests
     ├── api
     │   ├── __init__.py
     │   └── test_add.py
     ├── cli
     │   ├── __init__.py
     │   ├── conftest.py
     │   └── test_add.py
     └── conftest.py

Now we can test the add functionality both via the API and via the CLI, whereby a test_add.py is located in both directories:

$ pytest
============================= test session starts ==============================
...
rootdir: /Users/veit/cusy/prj/items
configfile: pyproject.toml
testpaths: tests
plugins: Faker-19.11.0
collected 6 items

tests/api/test_add.py ....                                               [ 66%]
tests/cli/test_add.py ..                                                 [100%]

============================== 6 passed in 0.03s ===============================

Most of my projects start with the following configuration:

addopts =
   --strict-markers
   --strict-config
   -ra