]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-123934: Fix `MagicMock` not to reset magic method return values (GH-124038...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 19 Sep 2024 08:18:01 +0000 (10:18 +0200)
committerGitHub <noreply@github.com>
Thu, 19 Sep 2024 08:18:01 +0000 (08:18 +0000)
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>
Lib/test/test_unittest/testmock/testmagicmethods.py
Lib/unittest/mock.py
Misc/NEWS.d/next/Library/2024-09-13-10-34-19.gh-issue-123934.yMe7mL.rst [new file with mode: 0644]

index a4feae7e9d3b73fbbec188f0cd6ec5df7f7066ee..a8b52ce48716f78661aa0de0fb6c4700127053a1 100644 (file)
@@ -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):
index 9398f56506b0aae8668cc2c9d7a8399577e29e62..805c6b3cea61409b0db34b471ede0659c1e5e3a6 100644 (file)
@@ -598,7 +598,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 = []
@@ -2189,6 +2189,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 (file)
index 0000000..cec7741
--- /dev/null
@@ -0,0 +1,2 @@
+Fix :class:`unittest.mock.MagicMock` reseting magic methods return values
+after ``.reset_mock(return_value=True)`` was called.