Skip to content

Instantly share code, notes, and snippets.

@peterhurford
Created July 28, 2016 15:48
Show Gist options
  • Save peterhurford/09f7dcda0ab04b95c026c60fa49c2a68 to your computer and use it in GitHub Desktop.
Save peterhurford/09f7dcda0ab04b95c026c60fa49c2a68 to your computer and use it in GitHub Desktop.
How to modularize your py.test fixtures

Using py.test is great and the support for test fixtures is pretty awesome. However, in order to share your fixtures across your entire module, py.test suggests you define all your fixtures within one single conftest.py file. This is impractical if you have a large quantity of fixtures -- for better organization and readibility, you would much rather define your fixtures across multiple, well-named files. But how do you do that? ...No one on the internet seemed to know.

Turns out, however, you can define fixtures in individual files like this:

tests/fixtures/add.py

import pytest

@pytest.fixture
def add(x, y):
    x + y

Then you can import these fixtures in your conftest.py:

tests/conftest.py

import pytest
from fixtures.add import add

...and then you're good to test!

tests/adding_test.py

import pytest

@pytest.mark.usefixtures("add")
def test_adding(add):
    assert add(2, 3) == 5

Because of the modularity, tests will have to be run with python -m py.test instead of py.test directly.

@joe-agent
Copy link

You can simply create "local pytest plugins" which can be nothing more than Python files with fixtures, e.g.

* tests/unit/conftest.py:
  ```python
  pytest_plugins = [
     "tests.unit.fixtures.some_stuff",
  ]
  • tests/unit/fixtures/some_stuff.py:
    import pytest
    
    @pytest.fixture
    def foo():
        return 'foobar'

Thanks for you idea @ikonst! Just made my conftest as package and add it as plugin!

Can you share how did you share the conftest as a package? Can that package be pip installed across other repo/projects?

@ikonst
Copy link

ikonst commented Apr 14, 2022

There's no much difference between a conftest.py and a pytest plugin (only one I can think of, is that a top-level conftest can have a pytest_plugins section).

If you want to make a Python package with pytest fixtures, see https://docs.pytest.org/en/6.2.x/writing_plugins.html

@kjhf
Copy link

kjhf commented Apr 26, 2023

Hello everyone, thank you for all the insightful comments.
If you use PyCharm, it seems that, for now, you won't be able to use the glob approach because of this issue, which cleans up the "unused import" needed with this approach.

My recommendation would be to use the hard-coded list and maintain the conftest.py file.
With this, you also do not need to import the fixture in your test file.

Otherwise, here's a summary of the above:

tests/conftest.py

from glob import glob
def _as_module(fixture_path: str) -> str:
    return fixture_path.replace("/", ".").replace("\\", ".").replace(".py", "")


pytest_plugins = [
    _as_module(fixture) for fixture in glob("tests/fixtures/[!_]*.py")
]

tests/fixtures/example.py

@pytest.fixture()
def example():
    return []

tests/unit/mytest.py

# If you did not hardcode pytest_plugins, you might need `from tests.fixtures.example import example`
def test_get_a_list(example):
    assert isinstance(example, list)

@rodrigoestevao
Copy link

pytest_plugins

It worked like a charm, thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment