1
2
3.. image:: http://img.shields.io/pypi/v/pytest-xdist.svg
4 :alt: PyPI version
5 :target: https://pypi.python.org/pypi/pytest-xdist
6
7.. image:: https://img.shields.io/conda/vn/conda-forge/pytest-xdist.svg
8 :target: https://anaconda.org/conda-forge/pytest-xdist
9
10.. image:: https://img.shields.io/pypi/pyversions/pytest-xdist.svg
11 :alt: Python versions
12 :target: https://pypi.python.org/pypi/pytest-xdist
13
14.. image:: https://travis-ci.org/pytest-dev/pytest-xdist.svg?branch=master
15 :alt: Travis CI build status
16 :target: https://travis-ci.org/pytest-dev/pytest-xdist
17
18.. image:: https://ci.appveyor.com/api/projects/status/56eq1a1avd4sdd7e/branch/master?svg=true
19 :alt: AppVeyor build status
20 :target: https://ci.appveyor.com/project/pytestbot/pytest-xdist
21
22.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
23 :target: https://github.com/ambv/black
24
25xdist: pytest distributed testing plugin
26========================================
27
28The `pytest-xdist`_ plugin extends pytest with some unique
29test execution modes:
30
31* test run parallelization_: if you have multiple CPUs or hosts you can use
32 those for a combined test run. This allows to speed up
33 development or to use special resources of `remote machines`_.
34
35
36* ``--looponfail``: run your tests repeatedly in a subprocess. After each run
37 pytest waits until a file in your project changes and then re-runs
38 the previously failing tests. This is repeated until all tests pass
39 after which again a full run is performed.
40
41* `Multi-Platform`_ coverage: you can specify different Python interpreters
42 or different platforms and run tests in parallel on all of them.
43
44Before running tests remotely, ``pytest`` efficiently "rsyncs" your
45program source code to the remote place. All test results
46are reported back and displayed to your local terminal.
47You may specify different Python versions and interpreters.
48
49If you would like to know how pytest-xdist works under the covers, checkout
50`OVERVIEW <https://github.com/pytest-dev/pytest-xdist/blob/master/OVERVIEW.md>`_.
51
52
53Installation
54------------
55
56Install the plugin with::
57
58 pip install pytest-xdist
59
60or use the package in develop/in-place mode with
61a checkout of the `pytest-xdist repository`_ ::
62
63 pip install --editable .
64
65.. _parallelization:
66
67Speed up test runs by sending tests to multiple CPUs
68----------------------------------------------------
69
70To send tests to multiple CPUs, type::
71
72 pytest -n NUM
73
74Especially for longer running tests or tests requiring
75a lot of I/O this can lead to considerable speed ups. This option can
76also be set to ``auto`` for automatic detection of the number of CPUs.
77
78If a test crashes the interpreter, pytest-xdist will automatically restart
79that worker and report the failure as usual. You can use the
80``--max-worker-restart`` option to limit the number of workers that can
81be restarted, or disable restarting altogether using ``--max-worker-restart=0``.
82
83By default, the ``-n`` option will send pending tests to any worker that is available, without
84any guaranteed order, but you can control this with these options:
85
86* ``--dist=loadscope``: tests will be grouped by **module** for *test functions* and
87 by **class** for *test methods*, then each group will be sent to an available worker,
88 guaranteeing that all tests in a group run in the same process. This can be useful if you have
89 expensive module-level or class-level fixtures. Currently the groupings can't be customized,
90 with grouping by class takes priority over grouping by module.
91 This feature was added in version ``1.19``.
92
93* ``--dist=loadfile``: tests will be grouped by file name, and then will be sent to an available
94 worker, guaranteeing that all tests in a group run in the same worker. This feature was added
95 in version ``1.21``.
96
97
98Making session-scoped fixtures execute only once
99^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
100
101``pytest-xdist`` is designed so that each worker process will perform its own collection and execute
102a subset of all tests. This means that tests in different processes requesting a high-level
103scoped fixture (for example ``session``) will execute the fixture code more than once, which
104breaks expectations and might be undesired in certain situations.
105
106While ``pytest-xdist`` does not have a builtin support for ensuring a session-scoped fixture is
107executed exactly once, this can be achieved by using a lock file for inter-process communication.
108
109The example below needs to execute the fixture ``session_data`` only once (because it is
110resource intensive, or needs to execute only once to define configuration options, etc), so it makes
111use of a `FileLock <https://pypi.org/project/filelock/>`_ to produce the fixture data only once
112when the first process requests the fixture, while the other processes will then read
113the data from a file.
114
115Here is the code:
116
117.. code-block:: python
118
119 import json
120
121 import pytest
122 from filelock import FileLock
123
124
125 @pytest.fixture(scope="session")
126 def session_data(tmp_path_factory, worker_id):
127 if not worker_id:
128 # not executing in with multiple workers, just produce the data and let
129 # pytest's fixture caching do its job
130 return produce_expensive_data()
131
132 # get the temp directory shared by all workers
133 root_tmp_dir = tmp_path_factory.getbasetemp().parent
134
135 fn = root_tmp_dir / "data.json"
136 with FileLock(str(fn) + ".lock"):
137 if fn.is_file():
138 data = json.loads(fn.read_text())
139 else:
140 data = produce_expensive_data()
141 fn.write_text(json.dumps(data))
142 return data
143
144
145The example above can also be use in cases a fixture needs to execute exactly once per test session, like
146initializing a database service and populating initial tables.
147
148This technique might not work for every case, but should be a starting point for many situations
149where executing a high-scope fixture exactly once is important.
150
151Running tests in a Python subprocess
152------------------------------------
153
154To instantiate a python3.5 subprocess and send tests to it, you may type::
155
156 pytest -d --tx popen//python=python3.5
157
158This will start a subprocess which is run with the ``python3.5``
159Python interpreter, found in your system binary lookup path.
160
161If you prefix the --tx option value like this::
162
163 --tx 3*popen//python=python3.5
164
165then three subprocesses would be created and tests
166will be load-balanced across these three processes.
167
168.. _boxed:
169
170Running tests in a boxed subprocess
171-----------------------------------
172
173This functionality has been moved to the
174`pytest-forked <https://github.com/pytest-dev/pytest-forked>`_ plugin, but the ``--boxed`` option
175is still kept for backward compatibility.
176
177.. _`remote machines`:
178
179Sending tests to remote SSH accounts
180------------------------------------
181
182Suppose you have a package ``mypkg`` which contains some
183tests that you can successfully run locally. And you
184have a ssh-reachable machine ``myhost``. Then
185you can ad-hoc distribute your tests by typing::
186
187 pytest -d --tx ssh=myhostpopen --rsyncdir mypkg mypkg
188
189This will synchronize your :code:`mypkg` package directory
190to a remote ssh account and then locally collect tests
191and send them to remote places for execution.
192
193You can specify multiple :code:`--rsyncdir` directories
194to be sent to the remote side.
195
196.. note::
197
198 For pytest to collect and send tests correctly
199 you not only need to make sure all code and tests
200 directories are rsynced, but that any test (sub) directory
201 also has an :code:`__init__.py` file because internally
202 pytest references tests as a fully qualified python
203 module path. **You will otherwise get strange errors**
204 during setup of the remote side.
205
206
207You can specify multiple :code:`--rsyncignore` glob patterns
208to be ignored when file are sent to the remote side.
209There are also internal ignores: :code:`.*, *.pyc, *.pyo, *~`
210Those you cannot override using rsyncignore command-line or
211ini-file option(s).
212
213
214Sending tests to remote Socket Servers
215--------------------------------------
216
217Download the single-module `socketserver.py`_ Python program
218and run it like this::
219
220 python socketserver.py
221
222It will tell you that it starts listening on the default
223port. You can now on your home machine specify this
224new socket host with something like this::
225
226 pytest -d --tx socket=192.168.1.102:8888 --rsyncdir mypkg mypkg
227
228
229.. _`atonce`:
230.. _`Multi-Platform`:
231
232
233Running tests on many platforms at once
234---------------------------------------
235
236The basic command to run tests on multiple platforms is::
237
238 pytest --dist=each --tx=spec1 --tx=spec2
239
240If you specify a windows host, an OSX host and a Linux
241environment this command will send each tests to all
242platforms - and report back failures from all platforms
243at once. The specifications strings use the `xspec syntax`_.
244
245.. _`xspec syntax`: http://codespeak.net/execnet/basics.html#xspec
246
247.. _`socketserver.py`: http://bitbucket.org/hpk42/execnet/raw/2af991418160/execnet/script/socketserver.py
248
249.. _`execnet`: http://codespeak.net/execnet
250
251Identifying the worker process during a test
252--------------------------------------------
253
254*New in version 1.15.*
255
256If you need to determine the identity of a worker process in
257a test or fixture, you may use the ``worker_id`` fixture to do so:
258
259.. code-block:: python
260
261 @pytest.fixture()
262 def user_account(worker_id):
263 """ use a different account in each xdist worker """
264 return "account_%s" % worker_id
265
266When ``xdist`` is disabled (running with ``-n0`` for example), then
267``worker_id`` will return ``"master"``.
268
269Additionally, worker processes have the following environment variables
270defined:
271
272* ``PYTEST_XDIST_WORKER``: the name of the worker, e.g., ``"gw2"``.
273* ``PYTEST_XDIST_WORKER_COUNT``: the total number of workers in this session,
274 e.g., ``"4"`` when ``-n 4`` is given in the command-line.
275
276The information about the worker_id in a test is stored in the ``TestReport`` as
277well, under the ``worker_id`` attribute.
278
279
280Uniquely identifying the current test run
281-----------------------------------------
282
283*New in version 1.32.*
284
285If you need to globally distinguish one test run from others in your
286workers, you can use the ``testrun_uid`` fixture. For instance, let's say you
287wanted to create a separate database for each test run:
288
289.. code-block:: python
290
291 import pytest
292 from posix_ipc import Semaphore, O_CREAT
293
294 @pytest.fixture(scope="session", autouse=True)
295 def create_unique_database(testrun_uid):
296 """ create a unique database for this particular test run """
297 database_url = f"psql://myapp-{testrun_uid}"
298
299 with Semaphore(f"/{testrun_uid}-lock", flags=O_CREAT, initial_value=1):
300 if not database_exists(database_url):
301 create_database(database_url)
302
303 @pytest.fixture()
304 def db(testrun_uid):
305 """ retrieve unique database """
306 database_url = f"psql://myapp-{testrun_uid}"
307 return database_get_instance(database_url)
308
309
310Additionally, during a test run, the following environment variable is defined:
311
312* ``PYTEST_XDIST_TESTRUNUID``: the unique id of the test run.
313
314Accessing ``sys.argv`` from the master node in workers
315------------------------------------------------------
316
317To access the ``sys.argv`` passed to the command-line of the master node, use
318``request.config.workerinput["mainargv"]``.
319
320
321Specifying test exec environments in an ini file
322------------------------------------------------
323
324You can use pytest's ini file configuration to avoid typing common options.
325You can for example make running with three subprocesses your default like this:
326
327.. code-block:: ini
328
329 [pytest]
330 addopts = -n3
331
332You can also add default environments like this:
333
334.. code-block:: ini
335
336 [pytest]
337 addopts = --tx ssh=myhost//python=python3.5 --tx ssh=myhost//python=python3.6
338
339and then just type::
340
341 pytest --dist=each
342
343to run tests in each of the environments.
344
345
346Specifying "rsync" dirs in an ini-file
347--------------------------------------
348
349In a ``tox.ini`` or ``setup.cfg`` file in your root project directory
350you may specify directories to include or to exclude in synchronisation:
351
352.. code-block:: ini
353
354 [pytest]
355 rsyncdirs = . mypkg helperpkg
356 rsyncignore = .hg
357
358These directory specifications are relative to the directory
359where the configuration file was found.
360
361.. _`pytest-xdist`: http://pypi.python.org/pypi/pytest-xdist
362.. _`pytest-xdist repository`: https://github.com/pytest-dev/pytest-xdist
363.. _`pytest`: http://pytest.org
364