@@ -95,6 +95,59 @@ any guaranteed order, but you can control this with these options:
95
95
in version ``1.21 ``.
96
96
97
97
98
+ Making 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
102
+ a subset of all tests. This means that tests in different processes requesting a high-level
103
+ scoped fixture (for example ``session ``) will execute the fixture code more than once, which
104
+ breaks expectations and might be undesired in certain situations.
105
+
106
+ While ``pytest-xdist `` does not have a builtin support for ensuring a session-scoped fixture is
107
+ executed exactly once, this can be achieved by using a lock file for inter-process communication.
108
+
109
+ The example below needs to execute the fixture ``session_data `` only once (because it is
110
+ resource intensive, or needs to execute only once to define configuration options, etc), so it makes
111
+ use of a `FileLock <https://pypi.org/project/filelock/ >`_ to produce the fixture data only once
112
+ when the first process requests the fixture, while the other processes will then read
113
+ the data from a file.
114
+
115
+ Here 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 for 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
+
145
+ The example above can also be use in cases a fixture needs to execute exactly once per test session, like
146
+ initializing a database service and populating initial tables.
147
+
148
+ This technique might not work for every case, but should be a starting point for many situations
149
+ where executing a high-scope fixture exactly once is important.
150
+
98
151
Running tests in a Python subprocess
99
152
------------------------------------
100
153
0 commit comments