What is conftest.py for in Pytest?
Asked Answered
L

6

572

I'm trying to understand what conftest.py files are meant to be used for.

In my (currently small) test suite I have one conftest.py file at the project root. I use it to define the fixtures that I inject into my tests.

I have two questions:

  1. Is this the correct use of conftest.py? Does it have other uses?
  2. Can I have more than one conftest.py file? When would I want to do that? Examples will be appreciated.

More generally, how would you define the purpose and correct use of conftest.py file(s) in a pytest test suite?

Literator answered 25/12, 2015 at 20:8 Comment(2)
You had me at It seems great. However, I feel the documentation could be better.Spinster
Yeah, the documentation could be so much better. I searched the entire pytest documentation for conftest.py and although there are many references to doing this thing or doing that thing with a conftest file, nowhere in the documentation does it ever indicate that when pytest does test discovery, all conftest.py files found (within the directory strucutre over which test discovery is being done) will be run during the test collection phase (before any tests are run). Had to figure that out myself via experimentation.Chris
L
659

Is this the correct use of conftest.py?

Yes it is. Fixtures are a potential and common use of conftest.py. The fixtures that you will define will be shared among all tests in your test suite. However, defining fixtures in the root conftest.py might be useless and it would slow down testing if such fixtures are not used by all tests.

Does it have other uses?

Yes it does.

  • Fixtures: Define fixtures for static data used by tests. This data can be accessed by all tests in the suite unless specified otherwise. This could be data as well as helpers of modules which will be passed to all tests.

  • External plugin loading: conftest.py is used to import external plugins or modules. By defining the following global variable, pytest will load the module and make it available for its test. Plugins are generally files defined in your project or other modules which might be needed in your tests. You can also load a set of predefined plugins as explained here.

    pytest_plugins = "someapp.someplugin"

  • Hooks: You can specify hooks such as setup and teardown methods and much more to improve your tests. For a set of available hooks, read Hooks link. Example:

      def pytest_runtest_setup(item):
           """ called before ``pytest_runtest_call(item). """
           #do some stuff`
    
  • Test root path: This is a bit of a hidden feature. By defining conftest.py in your root path, you will have pytest recognizing your application modules without specifying PYTHONPATH. In the background, py.test modifies your sys.path by including all submodules which are found from the root path.

Can I have more than one conftest.py file?

Yes you can and it is strongly recommended if your test structure is somewhat complex. conftest.py files have directory scope. Therefore, creating targeted fixtures and helpers is good practice.

When would I want to do that? Examples will be appreciated.

Several cases could fit:

Creating a set of tools or hooks for a particular group of tests.

root/mod/conftest.py

def pytest_runtest_setup(item):
    print("I am mod")
    #do some stuff


test root/mod2/test.py will NOT produce "I am mod"

Loading a set of fixtures for some tests but not for others.

root/mod/conftest.py

@pytest.fixture()
def fixture():
    return "some stuff"

root/mod2/conftest.py

@pytest.fixture()
def fixture():
    return "some other stuff"

root/mod2/test.py

def test(fixture):
    print(fixture)

Will print "some other stuff".

Overriding hooks inherited from the root conftest.py.

root/mod/conftest.py

def pytest_runtest_setup(item):
    print("I am mod")
    #do some stuff

root/conftest.py

def pytest_runtest_setup(item):
    print("I am root")
    #do some stuff

By running any test inside root/mod, only "I am mod" is printed.

You can read more about conftest.py here.

EDIT:

What if I need plain-old helper functions to be called from a number of tests in different modules - will they be available to me if I put them in a conftest.py? Or should I simply put them in a helpers.py module and import and use it in my test modules?

You can use conftest.py to define your helpers. However, you should follow common practice. Helpers can be used as fixtures at least in pytest. For example in my tests I have a mock redis helper which I inject into my tests this way.

root/helper/redis/redis.py

@pytest.fixture
def mock_redis():
    return MockRedis()

root/tests/stuff/conftest.py

pytest_plugin="helper.redis.redis"

root/tests/stuff/test.py

def test(mock_redis):
    print(mock_redis.get('stuff'))

This will be a test module that you can freely import in your tests. NOTE that you could potentially name redis.py as conftest.py if your module redis contains more tests. However, that practice is discouraged because of ambiguity.

If you want to use conftest.py, you can simply put that helper in your root conftest.py and inject it when needed.

root/tests/conftest.py

@pytest.fixture
def mock_redis():
    return MockRedis()

root/tests/stuff/test.py

def test(mock_redis):
    print(mock_redis.get(stuff))

Another thing you can do is to write an installable plugin. In that case your helper can be written anywhere but it needs to define an entry point to be installed in your and other potential test frameworks. See this.

If you don't want to use fixtures, you could of course define a simple helper and just use the plain old import wherever it is needed.

root/tests/helper/redis.py

class MockRedis():
    # stuff

root/tests/stuff/test.py

from helper.redis import MockRedis

def test():
    print(MockRedis().get(stuff))

However, here you might have problems with the path since the module is not in a child folder of the test. You should be able to overcome this (not tested) by adding an __init__.py to your helper

root/tests/helper/init.py

from .redis import MockRedis

Or simply adding the helper module to your PYTHONPATH.

Levins answered 29/12, 2015 at 23:53 Comment(2)
Without conftest.py, your test root path does not get added to the sys.path, and you will see errors like "ModuleNotFoundError: No module named 'foobar'".Fantasia
This is very helpful... but I'm a little confused by the statement "py.test modifies your sys.path by including all submodules which are found from the root path". I have two dirs under the root path, tests and src. src has one dir/package under it, "core", where the .py files are. From examining sys.path in the __main__.py file there during testing I can see that although the root dir and the "tests" dir are listed, neither "src" nor "src/core" is... which is problematic because it means __main__.py can't import sibling .py files as it can during a normal run (= not testing).Nesmith
S
19

Update Pytest 7.2 (2023 - March)

Adding this answer because there are some comment regarding missing information in the docs, which I guess was back when this question was first asked, but now is very well explained and documented.

From conftest.py: sharing fixtures across multiple files till the bottom of the page (and actually anything related to fixture or setting up pytest can be applied to the conftest.py) is related to conftest.py

Therefore, by quickly scanning the docs these are the main functionality (but not inclusive) :

  • Serve fixtures to tests (no matter the fixture scope)
  • Modularize the available fixture for each conftest.py in a Tree like hierarchy
  • Scope: sharing fixtures across classes, modules, packages or session
  • Child conftest.py can override parent conftest.py (but not the opposite) See this
  • Serve fixtures from a third-party plugins
  • Loading and Sharing test data across multiple tests
  • Using fixtures from other projects
  • Serve and share Hooks

Now on the accepted answer, it says Test root path: : This is a bit of a hidden feature. By defining ... but as for now, the best way to do this is instead add pythonpath to a pytest.ini on the root of your project (the actual project root, not project/tests ) see: pythonpath and does all for you and is more powerfull

Which looks like

[pytest]
pythonpath = src1 src2

Lastly, here I drop one of the many graphs that are in the pytest docs about The boundaries of the scopes can be visualized like this:

enter image description here

Spurlock answered 22/3, 2023 at 8:3 Comment(0)
P
18

In a wide meaning conftest.py is a local per-directory plugin. Here you define directory-specific hooks and fixtures. In my case a have a root directory containing project specific tests directories. Some common magic is stationed in 'root' conftest.py. Project specific - in their own ones. Can't see anything bad in storing fixtures in conftest.py unless they are not used widely (In that case I prefer to define them in test files directly)

Pears answered 28/12, 2015 at 12:55 Comment(0)
P
16

I use the conftest.py file to define the fixtures that I inject into my tests, is this the correct use of conftest.py?

Yes, a fixture is usually used to get data ready for multiple tests.

Does it have other uses?

Yes, a fixture is a function that is run by pytest before, and sometimes after, the actual test functions. The code in the fixture can do whatever you want it to. For instance, a fixture can be used to get a data set for the tests to work on, or a fixture can also be used to get a system into a known state before running a test.

Can I have more than one conftest.py file? When would I want to do that?

First, it is possible to put fixtures into individual test files. However, to share fixtures among multiple test files, you need to use a conftest.py file somewhere centrally located for all of the tests. Fixtures can be shared by any test. They can be put in individual test files if you want the fixture to only be used by tests in that file.

Second, yes, you can have other conftest.py files in subdirectories of the top tests directory. If you do, fixtures defined in these lower-level conftest.py files will be available to tests in that directory and subdirectories.

Finally, putting fixtures in the conftest.py file at the test root will make them available in all test files.

Pajamas answered 7/8, 2018 at 3:47 Comment(0)
S
11

Here are the official docs about using conftest.py to share fixtures:

conftest.py: sharing fixtures across multiple files

The conftest.py file serves as a means of providing fixtures for an entire directory. Fixtures defined in a conftest.py can be used by any test in that package without needing to import them (pytest will automatically discover them).

You can have multiple nested directories/packages containing your tests, and each directory can have its own conftest.py with its own fixtures, adding on to the ones provided by the conftest.py files in parent directories.

Strut answered 15/9, 2021 at 7:32 Comment(0)
C
4

You can have multiple conftest.py.

For example, tests1/test_1.py can only access tests1/conftest.py as shown below:

project
 |-tests1
 |  |-__init__.py
 |  |-conftest.py # <- 〇
 |  |-test_1.py # Here
 |  └-subtests
 |     |-__init__.py
 |     |-conftest.py # <- ✖
 |     └-test_2.py
 └-tests2
    |-__init__.py
    |-conftest.py # <- ✖
    |-test_1.py
    └-subtests
       |-__init__.py
       |-conftest.py # <- ✖
       └-test_2.py

And, tests1/subtests/test_2.py can only access tests1/subtests/conftest.py and tests1/conftest.py as shown below. * If there are the same name fixtures(functions) in tests1/subtests/conftest.py and tests1/conftest.py, then the fixture(function) in tests1/subtests/conftest.py is prioritized to access by tests1/subtests/test_2.py:

project
 |-tests1
 |  |-__init__.py
 |  |-conftest.py # <- 〇
 |  |-test_1.py 
 |  └-subtests
 |     |-__init__.py
 |     |-conftest.py # <- 〇
 |     └-test_2.py # Here
 └-tests2
    |-__init__.py
    |-conftest.py # <- ✖
    |-test_1.py
    └-subtests
       |-__init__.py
       |-conftest.py # <- ✖
       └-test_2.py
Calends answered 3/9, 2023 at 5:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.