Skip to content

Commit f551c5b

Browse files
chaopemiss-islington
authored andcommitted
pythongh-142651: make Mock.call_count thread-safe (pythonGH-142656)
(cherry picked from commit 850f95f) Co-authored-by: chaope <[email protected]>
1 parent 7957bfd commit f551c5b

File tree

3 files changed

+27
-2
lines changed

3 files changed

+27
-2
lines changed

Lib/test/test_unittest/testmock/testthreadingmock.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import sys
12
import time
23
import unittest
4+
import threading
35
import concurrent.futures
46

5-
from test.support import threading_helper
7+
from test.support import setswitchinterval, threading_helper
68
from unittest.mock import patch, ThreadingMock
79

810

@@ -196,6 +198,26 @@ def test_reset_mock_resets_wait(self):
196198
m.wait_until_any_call_with()
197199
m.assert_called_once()
198200

201+
def test_call_count_thread_safe(self):
202+
# See https://github.com/python/cpython/issues/142651.
203+
m = ThreadingMock()
204+
LOOPS = 100
205+
THREADS = 10
206+
def test_function():
207+
for _ in range(LOOPS):
208+
m()
209+
210+
oldswitchinterval = sys.getswitchinterval()
211+
setswitchinterval(1e-6)
212+
try:
213+
threads = [threading.Thread(target=test_function) for _ in range(THREADS)]
214+
with threading_helper.start_threads(threads):
215+
pass
216+
finally:
217+
sys.setswitchinterval(oldswitchinterval)
218+
219+
self.assertEqual(m.call_count, LOOPS * THREADS)
220+
199221

200222
if __name__ == "__main__":
201223
unittest.main()

Lib/unittest/mock.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1174,14 +1174,14 @@ def _mock_call(self, /, *args, **kwargs):
11741174

11751175
def _increment_mock_call(self, /, *args, **kwargs):
11761176
self.called = True
1177-
self.call_count += 1
11781177

11791178
# handle call_args
11801179
# needs to be set here so assertions on call arguments pass before
11811180
# execution in the case of awaited calls
11821181
_call = _Call((args, kwargs), two=True)
11831182
self.call_args = _call
11841183
self.call_args_list.append(_call)
1184+
self.call_count = len(self.call_args_list)
11851185

11861186
# initial stuff for method_calls:
11871187
do_method_calls = self._mock_parent is not None
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:mod:`unittest.mock`: fix a thread safety issue where :attr:`Mock.call_count
2+
<unittest.mock.Mock.call_count>` may return inaccurate values when the mock
3+
is called concurrently from multiple threads.

0 commit comments

Comments
 (0)