Splitting a conftest.py file into several smaller conftest-like parts

Question:

I’ve got a large conftest.py file that I wish to split into smaller parts, for two reasons:

  1. The file is very large (~1000 lines, including documentation)
  2. Some of the fixtures depend on other fixtures, and I have no reason to expose those other fixtures as part of the conftest “API” when users look for relevant fixtures

I am not aware of any mechanism provided by pytest to resolve conftest files in multiple locations within the same folder, so I contrived one, below:

import sys
import os


sys.path.append(os.path.dirname(__file__))


from _conftest_private_part_1 import *
from _conftest_private_part_2 import *
from _conftest_private_part_3 import *


@pytest.fixture
def a_fixture_that_is_part_of_the_public_conftest_api():
    pass

This works for my needs, but I do wonder if there is a better way.

Asked By: alkalinity

||

Answers:

You shouldn’t need any fancy magic for that. py.test automatically adds the path of the current test file to sys.path, as well as all parent paths up to the directory it was targeted at.

Because of that, you don’t even need to put that shared code into a conftest.py. You can just put into plain modules or packages and then import it (if you want to share fixtures, those have to be in a conftest.py).

Also, there is this note about importing from conftest.py in the documentation:

If you have conftest.py files which do not reside in a python package
directory (i.e. one containing an __init__.py) then “import conftest
can be ambiguous because there might be other conftest.py files as
well on your PYTHONPATH or sys.path. It is thus good practise for
projects to either put conftest.py under a package scope or to never
import anything from a conftest.py file.

Answered By: Björn Pollex

You can put your stuff in other modules and reference them using a pytest_plugins variable in your conftest.py:

pytest_plugins = ['module1', 'module2']

This will also work if your conftest.py has hooks on them.

Answered By: Bruno Oliveira

This works for me and seems easier/clearer:

Top level tests/conftest.py (example of re-usable print debug of requests.Response):

import pytest
import requests
from requests_toolbelt.utils import dump


@pytest.fixture(scope="session")
def print_response(response: requests.Response):
    data = dump.dump_all(response)
    print("========================")
    print(data.decode('utf-8'))
    print("========================")

    print("response.url = {}".format(response.url))
    print("response.request = {}".format(response.request))
    print("response.status_code = {}".format(response.status_code))
    print("response.headers['content-type'] = {}".format(response.headers['content-type']))
    print("response.encoding = {}".format(response.encoding))
    try:
        print("response.json = {}".format(response.json()))
    except Exception:
        print("response.text = {}".format(response.text))
    print("response.end")

From lower level conftest import the higher level conftest code – e.g., tests/package1/conftest.py:

from tests.conftest import *

Then, in your lower level tests within tests/package1/test_*.py, you simply import via:

from tests.package1 import conftest

And then you have the merged configtests from one conftest available. Repeat this pattern for your other lower level detailed/modular conftest.py files throughout the tests hierarchy.

Answered By: J.D.

Alternativelly, you could try:

In my_fixtures.py:

@pytest.fixture
def some_fixture():
  yield

In conftest.py:

import my_fixtures


# Adding the fixture to attributes of `conftest` will register it
some_fixture = my_fixtures.some_fixture

It seems that pytest detect fixture by iterating over conftest attributes and checking for some attr._pytestfixturefunction added by @pytest.fixture.

So as long as conftest.py contains fixture attributes, it doesn’t really matter which file is the fixture defined.

I haven’t tried it but I would expect the following to work too:

# In `conftest.py`
from my_fixtures import *
Answered By: Conchylicultor
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.