Search code examples
pythonpython-3.xpytest

Pytest running very slow for project


My pytest setup is running very slow, especially during collection phase.

So I have put up a pytest setup for my Django project, and each Django app's test-files are located in its own folder, i.e., the tree looks as follows

root
|
+--a
|  |
|  +--tests
|       |
|       +--conftest.py
|       +--testAa.py
|       +--testBa.py
+--b
|  |
|  +--tests
|       |
|       +--conftest.py
|       +--testAb.py
|       +--testBb.py
...
pytest.ini

The pytest.ini file specifies where to look for the tests and has the following content

[pytest]
DJANGO_SETTINGS_MODULE = project.project.settings.test
python_files = tests.py test_*.py *_tests.py
addopts = --reuse-db

For each app within the tests folder I have a file called contest.py. This file creates a set of objects that are used multiple times in the test files. For instance if an object of class A is used more than once, a contest creates that variable once and the tests use this conftest as input. Each conftest has the decorator @pytest.fixture(scope="function"), and the tests have the decorator @pytest.mark.django_db.

I don't think the loading times are caused by the conftests, or the decorators discussed in the last paragraph, but rather the tree structure and pytest.ini file I have put up. Are there any rules for what is a good structure for this? As I said, the loading times are extremely high for collecting the tests. To be more accurate, I have about 80 tests, and collecting them takes about 40 seconds. Running them all takes an additional 20.


Solution

  • More or less restating my other answer:

    When you invoke pytest, it will scan every subdirectory in project root, looking for tests. This may slow down the test collection; it may be wise to exclude unrelated directories from being scanned. pytest offers two configuration options for that:

    1. norecursedirs - holds directories that will be excluded from scanning. Use this option when you are looking for the pattern "include all, exclude selected". By default, norecursedirs is set to '.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg', so beware that when you override this option, the defaults are gone and you have to add them back.
    2. testpaths - holds directories that should only be considered for the scan, so this is basically the opposite to what norecursedirs is doing. Use this option when looking for the pattern "exclude all, include selected". This option also adds some minor or more significant speedup to the test discovery, depending on what you keep in the project root - most of the sudbirectories won't be traversed at all and the tests run starts sooner.

    Usage

    You have several options:

    1. Place the options in pyproject.toml:

      [tool.pytest.ini_options]
      testpaths = [ "tests", "othertests" ]
      
    2. Place the options in the pytest.ini/setup.cfg/tox.ini:

      [tool:pytest]
      testpaths = tests othertests doc
      
    3. Pass them via --override-ini from command line.

      pytest -o "testpaths=tests othertests doc" ...