avatarAndy Barnes

Summary

The website content provides four key strategies to optimize and expedite pytest test suites, focusing on fixture scope, parallelization, profiling, and selective skipping of slow tests.

Abstract

The article outlines several techniques to enhance the performance of pytest test suites. It emphasizes the importance of correctly using fixture scopes to prevent unnecessary test duration extensions. It introduces parallelization through the pytest-xdist plugin, allowing tests to run simultaneously across multiple CPUs to reduce overall execution time. The article also suggests regular profiling of tests to identify and address the slowest ones, using tools like pytest-profiling or Python's cProfile. Additionally, it proposes the selective skipping of stable but slow tests during local development, with the option to re-enable them in the CI pipeline, using the pytest-skip-slow extension. These strategies aim to make test times more manageable and improve the efficiency of the testing process.

Opinions

  • The author believes that slow tests should be identified and optimized rather than accepted as inevitable.
  • The use of the pytest-xdist plugin for parallelization is recommended as an easy and effective way to speed up test execution.
  • Profiling is considered essential for maintaining the health of a test suite and for pinpointing areas needing improvement.
  • The author suggests that not all tests need to be run in every development cycle, particularly if they are known to be slow and stable.
  • The pytest-skip-slow extension is endorsed for balancing the need for comprehensive testing with the practicalities of development time constraints.

Pytest — How to speed up your tests

This post will cover several techniques that you can use to speed up your pytest suite to hopefully make your test times a little more bearable.

Two of the approaches below are aimed at fixing slow tests which shouldn’t be slow and the other two focus on speeding up genuinely slow test suites.

1. Correct Fixture Scope

I’ve already written a comprehensive post on fixture scope usage, breaking down how it can easily inflate your test durations when not used correctly. You can check it out here.

2. Parallelisation

Traditionally, pytest suites operate sequentially, running each test one by one. This is fine for small or fast test suites but can become tedious for larger, more complex projects. An easy way to address this is through the use of the pytest-xdist plugin, which allows you to parallelise your tests, by distributing them across multiple CPUs.

pip install pytest-xdist
pytest -n auto

Once installed, add -n auto to your existing pytest command to automatically spawn multiple workers equal to the number of available CPUs.

# With pytest-xdist
460 passed, 7 skipped, 224 warnings, 25 subtests passed in 68.64s (0:01:08)
# Running normally
460 passed, 7 skipped, 26 warnings, 25 subtests passed in 216.57s (0:03:36)

3. Profiling

Are there a couple of tests that are responsible for the majority of your test time? You can’t fix a problem if you don’t know where it is or even worse, you didn’t know there was one in the first place. Rather than accepting slow tests, profile them regularly to see if there’s anything that stands out. This is trivial to do in pytest.

pytest --durations 10

The argument --durations 10 will run your tests as normal but then print out the slowest 10 tests for you to inspect further. It shows whether it was the test setup or the test itself that took a long time to execute. The value 10 can be replaced with whatever number of tests you would like to display.

slowest 10 durations
20.57s setup   a/test_foo.py
8.69s call     b/test_foo.py
8.64s call     a/test_foo.py
7.09s setup    a/test_foo.py
7.03s setup    b/test_foo.py
5.63s setup    a/test_foo.py
5.36s call     b/test_foo.py
5.33s call     a/test_foo.py
5.27s call     b/test_foo.py
5.26s call     b/test_foo.py

Some tests will genuinely need to take a long time, whether the setup is cumbersome or it's doing some heavy processing. Any unexpected slow tests identified will either be doing some silly that can be fixed immediately or will need further, individual profiling. For this, I would recommend pytest-profiling or the cProfile functionality bundled with the Python standard library.

4. Skip Slow

If some legitimately slow tests are fairly stable and not likely to change or fail, they can be skipped during local development and then re-enabled when necessary. This would usually beat a certain stage of your CI pipeline if you have one. This can remove friction from your local development workflow while still keeping the layer of assurance of all tests passing before any code is merged.

This can easily be achieved through the use of the pytest-skip-slow extension. Once installed, you can mark specific slow tests with the @pytest.mark.slow decorator, which will cause them to be skipped on normal test runs, saving time. When you want to run the full test suite, including the slow ones, simply pass the --slow argument to pytest.

pytest --slow

Summary

Hopefully, the suggestions above can help improve or speed up your test suites. For a full breakdown of all the other checks you can do to speed up your tests, check out the repository below, which I found incredibly useful.

Python
Software Development
Software Testing
Pytest
Recommended from ReadMedium