Skip to content

Commit

Permalink
pythongh-123934: Fix MagicMock not to reset magic method return val…
Browse files Browse the repository at this point in the history
…ues (pythonGH-124038)

(cherry picked from commit 7628f67)

Co-authored-by: sobolevn <mail@sobolevn.me>
  • Loading branch information
sobolevn authored and miss-islington committed Sep 19, 2024
1 parent 112b170 commit ea1de1b
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 1 deletion.
39 changes: 39 additions & 0 deletions Lib/test/test_unittest/testmock/testmagicmethods.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,45 @@ def test_magic_methods_fspath(self):
self.assertEqual(os.fspath(mock), expected_path)
mock.__fspath__.assert_called_once()

def test_magic_mock_does_not_reset_magic_returns(self):
# https://github.com/python/cpython/issues/123934
for reset in (True, False):
with self.subTest(reset=reset):
mm = MagicMock()
self.assertIs(type(mm.__str__()), str)
mm.__str__.assert_called_once()

self.assertIs(type(mm.__hash__()), int)
mm.__hash__.assert_called_once()

for _ in range(3):
# Repeat reset several times to be sure:
mm.reset_mock(return_value=reset)

self.assertIs(type(mm.__str__()), str)
mm.__str__.assert_called_once()

self.assertIs(type(mm.__hash__()), int)
mm.__hash__.assert_called_once()

def test_magic_mock_resets_manual_mocks(self):
mm = MagicMock()
mm.__iter__ = MagicMock(return_value=iter([1]))
mm.custom = MagicMock(return_value=2)
self.assertEqual(list(iter(mm)), [1])
self.assertEqual(mm.custom(), 2)

mm.reset_mock(return_value=True)
self.assertEqual(list(iter(mm)), [])
self.assertIsInstance(mm.custom(), MagicMock)

def test_magic_mock_resets_manual_mocks_empty_iter(self):
mm = MagicMock()
mm.__iter__.return_value = []
self.assertEqual(list(iter(mm)), [])

mm.reset_mock(return_value=True)
self.assertEqual(list(iter(mm)), [])

def test_magic_methods_and_spec(self):
class Iterable(object):
Expand Down
13 changes: 12 additions & 1 deletion Lib/unittest/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,7 @@ def __set_side_effect(self, value):
side_effect = property(__get_side_effect, __set_side_effect)


def reset_mock(self, visited=None,*, return_value=False, side_effect=False):
def reset_mock(self, visited=None, *, return_value=False, side_effect=False):
"Restore the mock object to its initial state."
if visited is None:
visited = []
Expand Down Expand Up @@ -2219,6 +2219,17 @@ def mock_add_spec(self, spec, spec_set=False):
self._mock_add_spec(spec, spec_set)
self._mock_set_magics()

def reset_mock(self, /, *args, return_value=False, **kwargs):
if (
return_value
and self._mock_name
and _is_magic(self._mock_name)
):
# Don't reset return values for magic methods,
# otherwise `m.__str__` will start
# to return `MagicMock` instances, instead of `str` instances.
return_value = False
super().reset_mock(*args, return_value=return_value, **kwargs)


class MagicProxy(Base):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix :class:`unittest.mock.MagicMock` reseting magic methods return values
after ``.reset_mock(return_value=True)`` was called.

0 comments on commit ea1de1b

Please sign in to comment.