Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 766
Sindre Bakke Øyen Avatar asked Apr 25 '19 18:04

Sindre Bakke Øyen


People also ask

Why is pytest taking so long?

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.

How can I make pytest run faster?

Using the norecursedirs option in pytest. ini or tox. ini can save a lot of collection time, depending on what other files you have in your working directory. My collection time is roughly halved for a suite of 300 tests when I have that in place (0.34s vs 0.64s).

How do I stop pytest from running?

pytest has the option -x or --exitfirst which stops the execution of the tests instanly on first error or failed test. pytest also has the option --maxfail=num in which num indicates the number of errors or failures required to stop the execution of the tests.

What is pytest Conftest py?

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).

Why does pytest take so long to run?

E.g. pytest --collect-only root/a/tests. Maybe you have some large dir in project root which slows down the scan. If this is the case, you can restrict the directories via norecursedirs or testpaths options. It runs in 0.03 seconds in that case. I will try your second comment tomorrow, but it's getting late now. Thanks :)

How do I run a pytest test?

More generally, pytest follows standard test discovery rules. Pytest supports several ways to run and select tests from the command-line. This will run tests which contain names that match the given string expression (case-insensitive), which can include Python operators that use filenames, class names and function names as variables.

Why is my pytest collection phase so slow?

Check that you do not have own code which interact with pytest collection phase and make it slower. If not, you can run profiler like line profiler to see which code is slow. What code would interact with collection phase? How long does collection take in your setup?

How to show test durations that are too small in pytest?

By default, pytest will not show test durations that are too small (<0.005s) unless -vv is passed on the command-line. You can early-load plugins (internal and external) explicitly in the command-line with the -p option:


2 Answers

For me the issue was with a particular conftest.py taking a very long time to run (it was setting up some huge fixtures for a different test that I didn't need here).

This was tricky to diagnose, since the delay still occured with --collect-only flag and custom rootdir set to the test directory only (the conftest was in a different directory far away but was still being detected somehow).

The solution for me was to run pytest --noconftest.

like image 168
maxymoo Avatar answered Oct 19 '22 19:10

maxymoo


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: either place the options in the pytest.ini/setup.cfg/tox.ini:

[tool:pytest]
testpaths = tests othertests doc

or pass them via --override-ini from command line.

pytest -o "testpaths=tests othertests doc" ...
like image 43
hoefling Avatar answered Oct 19 '22 19:10

hoefling