pytest-portion
Select a portion of the collected tests, so you can run different parts of your test suite in different instances to scale horizontally.
Use case
Suppose you have a big, slow test suite, but you can trigger several CI workers to run different portions of it, in a sake lazy/simple way to parallelize it.
A basic, obvious way to do that is to explictily collect from different directories/modules:
- worker1:
pytest tests/a(100 tests, ~4 minutes to finish) - worker2:
pytest tests/b(20 tests, ~1 minute to finish) - worker3:
pytest tests/c tests/d(30 tests, ~1 minute to finish)
The problem is that directory tests/a may have a lot more tests that tests/c plus test/d,
so worker1 takes a lot more to finish.
With pytest-portion you can still split the tests in different instances, but letting
the extension makes the selection in a more balanced way.
- worker1:
pytest --portion 1/3 tests(first 50 tests, ~2 minutes) - worker2:
pytest --portion 2/3 tests(next 50 tests, ~2 minutes) - worker3:
pytest --portion 3/3 tests(last 50 tests, ~2 minutes)
In this case, the tests of all the directories are collected, but only a third (a different one!) of them will be actually executed on each worker.
Note this balance is by number of tests, so if there is very slow tests in a particular portion, the duration may not be expected.
For a fine tuning, you could pass the portion in a more explicit way:
- worker1:
pytest --portion 0:0.5 tests(first half, 1st to 75th test) - worker2:
pytest --portion 0.5:0.8 tests(next 30%, from 76th to 125th) - worker3:
pytest --portion 0.8:1 tests(last 20%)
Installation
You can add "pytest-portion" to your project from PyPI with uv.
uv add --dev pytetest-portion
Or via pip
pip install pytest-portion
Usage
There are three modes of operation: Test-level (default), File-level, and Function-level.
1. Test-level Slicing (Default)
Pytest collects all tests first, then pytest-portion filters them.
Pass --portion <i/n> where:
nis the total number of portions.iis the i-th portion to select (1 <= i <= n).
Note: If the number of tests collected is not divisible by
n, the last portion will contain the rest. For instance, if you havetest_1,test_2andtest_3,--portion 1/2will run the first one, and--portion 2/2the last 2.
Alternatively, use --portion start:end where start and end are coefficients between 0 and 1.
2. File-level Slicing
For very large projects, collection itself can be slow. Use --portion-files to slice the list of
discovered files before pytest starts collecting tests from within them. This can significantly
reduce collection time in large repositories.
# Collect and run only the files belonging to the first half of the suite
pytest --portion 1/2 --portion-files tests/
3. Function-level Slicing
With multiple parametrized test functions (e.g. test_compare1[...], test_compare2[...]), the default splits the entire collected list. That can put all of one function in portion 1 and all of another in portion 2, so workers get uneven loads.
Use --portion-functions to split within each test function instead: each function’s cases are portioned separately, so every function contributes proportionally to the selected set.
# Half from test_compare1 and half from test_compare2
pytest --portion 1/2 --portion-functions tests/
Note:
--portion-filesand--portion-functionscannot be used together.
Contributing
Contributions are very welcome. Please ensure the coverage at least stays the same before you submit a pull request.
License
Distributed under the terms of the BSD-3 license, "pytest-portion" is free and open source software.
Issues
If you encounter any problems, please file an issue along with a detailed description.
Acknowledgements
I used cookiecutter along with @hackebrot's cookiecutter-pytest-plugin template for the boilerplate code of this package. Thanks!
Release files for pytest-portion 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pytest_portion-0.3.0.tar.gz | 60.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pytest_portion-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 68.2 kB
Release files / pytest_portion-0.3.0.tar.gz
| Download URL | pytest_portion-0.3.0.tar.gz |
|---|---|
| Size | 60.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a1e7e8bd01fa8146bb33faff2030075546ee3be131c8573b4ad0aaaff43f2001
|
|
BLAKE2b-256 checksum How to use checksums |
202c7635034c170f1379ce47ab69a56cc2bd7f3d930c80ae1210be92eea5bcb7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / pytest_portion-0.3.0-py3-none-any.whl
| Download URL | pytest_portion-0.3.0-py3-none-any.whl |
|---|---|
| Size | 7.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
020fb36dce7f0f52f5765cacec7bac34759492aece67f55fa3a50d4e110192b6
|
|
BLAKE2b-256 checksum How to use checksums |
98f41b2e017a5b6fd499451235f4c9eddc5aaf357e39949ddd7f336aec2546a8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|