summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2024-09-30 01:11:03 (GMT)
committerGitHub <noreply@github.com>2024-09-30 01:11:03 (GMT)
commit0a868db982f1331f3c9b88f9b611b9050436a43c (patch)
treec37a632040326ffe47f135197b062a3614a92861
parentdceac5b8ea064671aa07b6cb35842955684555f1 (diff)
downloadcpython-0a868db982f1331f3c9b88f9b611b9050436a43c.zip
cpython-0a868db982f1331f3c9b88f9b611b9050436a43c.tar.gz
cpython-0a868db982f1331f3c9b88f9b611b9050436a43c.tar.bz2
[3.13] gh-123934: Fix `MagicMock` not to reset magic method return values (GH-124038) (#124231)
gh-123934: Fix `MagicMock` not to reset magic method return values (GH-124038) (cherry picked from commit 7628f67d55cb65bad9c9266e0457e468cd7e3775) Co-authored-by: sobolevn <mail@sobolevn.me>
-rw-r--r--Lib/test/test_unittest/testmock/testmagicmethods.py39
-rw-r--r--Lib/unittest/mock.py13
-rw-r--r--Misc/NEWS.d/next/Library/2024-09-13-10-34-19.gh-issue-123934.yMe7mL.rst2
3 files changed, 53 insertions, 1 deletions
diff --git a/Lib/test/test_unittest/testmock/testmagicmethods.py b/Lib/test/test_unittest/testmock/testmagicmethods.py
index a4feae7..a8b52ce 100644
--- a/Lib/test/test_unittest/testmock/testmagicmethods.py
+++ b/Lib/test/test_unittest/testmock/testmagicmethods.py
@@ -331,6 +331,45 @@ class TestMockingMagicMethods(unittest.TestCase):
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):
diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index f7ab4a5..da7da87 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -628,7 +628,7 @@ class NonCallableMock(Base):
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 = []
@@ -2219,6 +2219,17 @@ class MagicMock(MagicMixin, Mock):
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):
diff --git a/Misc/NEWS.d/next/Library/2024-09-13-10-34-19.gh-issue-123934.yMe7mL.rst b/Misc/NEWS.d/next/Library/2024-09-13-10-34-19.gh-issue-123934.yMe7mL.rst
new file mode 100644
index 0000000..cec7741
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-09-13-10-34-19.gh-issue-123934.yMe7mL.rst
@@ -0,0 +1,2 @@
+Fix :class:`unittest.mock.MagicMock` reseting magic methods return values
+after ``.reset_mock(return_value=True)`` was called.