7/31/2016

Py.test - Splitting conftest file

If you have to maintain a massive project, you probably have many fixtures in conftest file. And there is a problem; this file grows and grows. So, at some point, you decide to split this huge file into smaller files. But, py.test has to know the fixtures are keeping in these files.

So, what you can do? I see three patterns here for this:
  • import these guys inside conftest.py file
  • create more conftest.py files
  • use pytest_plugins

Import fixtures inside conftest file


You can for example do this:
1
2
3
4
5
6
7
8
9
from tests.my_fixtures.fixs1 import (
    fix11, fix12, fix13, fix14, fix15, ...
)
from tests.my_fixtures.fixs2 import (
    fix21, fix22, fix23,
)
from tests.my_fixtures.fixs3 import (
    fix31, fix32, fix33
)

But, there are some problems with this approach. You import them in an explicit way, so if you create a new fixture, you have to remember to add this guy in the import clause. So, this strategy is not the best solution. If you have many files with fixtures, it might hurt you. Every developer will have to remember that he/she has to append this fixture here.

Hence, you can import all of them:
1
2
3
from tests.my_fixtures.fixs1 import *
from tests.my_fixtures.fixs2 import *
from tests.my_fixtures.fixs3 import *

But, I don't think it is a good practice. For example, you can encounter some problems with pyflakes.

Create more conftest files


You could also create more conftest files. A straightforward solution is to separate tests for example into unit/integrations/ui and keep conftest files there.

For example:
1
2
3
tests/ui/conftest.py
tests/unit/conftest.py
tests/integration/conftest.py

Ok, but what if these files are still large? You want to split them again... We've discovered recently that we can use pytest_plugins for this.

Use pytest_plugins


How py.test discovers plugins (here's more info)?
  1. by loading all built-in plugins
  2. by loading all plugins registered through setuptools entry point
  3. by pre-scanning the command line for the -p name option
  4. by loading all conftest.py files
  5. by recursively loading all plugins specified by the pytest_plugins variable in conftest.py files
Look at the fifth item; you can load your fixtures by the pytest_plugins variable. You just have to add the pytest_plugins variable to your conftest.py file.

Let's say you had many fixtures in UI tests (in a conftest.py file). You split them into smaller modules, and now you want to have access to all of them during tests.

Here's example:
1
2
3
4
5
6
7
8
pytest_plugins = [
    ...
    'tests.ui.fixtures.user_fixtures',
    'tests.ui.fixtures.admin_fixtures',
    'tests.ui.fixtures.template_fixtures',
    'tests.ui.fixtures.free_account_fixtures',
    ...
]

If you create a new module with new fixtures, you have to add this guy here, but I think it is not a problem.