]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-25597: Ensure wraps' return value is used for magic methods in MagicMock (#16029)
authorKarthikeyan Singaravelan <tir.karthi@gmail.com>
Mon, 27 Jan 2020 06:48:15 +0000 (12:18 +0530)
committerChris Withers <chris@withers.org>
Mon, 27 Jan 2020 06:48:15 +0000 (06:48 +0000)
Lib/unittest/mock.py
Lib/unittest/test/testmock/testmock.py
Misc/NEWS.d/next/Library/2019-09-12-12-11-05.bpo-25597.mPMzVx.rst [new file with mode: 0644]

index beed717522bba3da0a14fe8ecf0591488b12df07..1acafc51df1d649257efd3fde9f469556cf70971 100644 (file)
@@ -2033,6 +2033,12 @@ _side_effect_methods = {
 
 
 def _set_return_value(mock, method, name):
+    # If _mock_wraps is present then attach it so that wrapped object
+    # is used for return value is used when called.
+    if mock._mock_wraps is not None:
+        method._mock_wraps = getattr(mock._mock_wraps, name)
+        return
+
     fixed = _return_values.get(name, DEFAULT)
     if fixed is not DEFAULT:
         method.return_value = fixed
index 1329346ae7246f1812f8ca3e6b22b70d4aa1a36f..677346725bdd244fe71af45ea24166aa48e3a038 100644 (file)
@@ -715,6 +715,53 @@ class MockTest(unittest.TestCase):
         self.assertRaises(StopIteration, mock.method)
 
 
+    def test_magic_method_wraps_dict(self):
+        data = {'foo': 'bar'}
+
+        wrapped_dict = MagicMock(wraps=data)
+        self.assertEqual(wrapped_dict.get('foo'), 'bar')
+        self.assertEqual(wrapped_dict['foo'], 'bar')
+        self.assertTrue('foo' in wrapped_dict)
+
+        # return_value is non-sentinel and takes precedence over wrapped value.
+        wrapped_dict.get.return_value = 'return_value'
+        self.assertEqual(wrapped_dict.get('foo'), 'return_value')
+
+        # return_value is sentinel and hence wrapped value is returned.
+        wrapped_dict.get.return_value = sentinel.DEFAULT
+        self.assertEqual(wrapped_dict.get('foo'), 'bar')
+
+        self.assertEqual(wrapped_dict.get('baz'), None)
+        with self.assertRaises(KeyError):
+            wrapped_dict['baz']
+        self.assertFalse('bar' in wrapped_dict)
+
+        data['baz'] = 'spam'
+        self.assertEqual(wrapped_dict.get('baz'), 'spam')
+        self.assertEqual(wrapped_dict['baz'], 'spam')
+        self.assertTrue('baz' in wrapped_dict)
+
+        del data['baz']
+        self.assertEqual(wrapped_dict.get('baz'), None)
+
+
+    def test_magic_method_wraps_class(self):
+
+        class Foo:
+
+            def __getitem__(self, index):
+                return index
+
+            def __custom_method__(self):
+                return "foo"
+
+
+        klass = MagicMock(wraps=Foo)
+        obj = klass()
+        self.assertEqual(obj.__getitem__(2), 2)
+        self.assertEqual(obj.__custom_method__(), "foo")
+
+
     def test_exceptional_side_effect(self):
         mock = Mock(side_effect=AttributeError)
         self.assertRaises(AttributeError, mock)
diff --git a/Misc/NEWS.d/next/Library/2019-09-12-12-11-05.bpo-25597.mPMzVx.rst b/Misc/NEWS.d/next/Library/2019-09-12-12-11-05.bpo-25597.mPMzVx.rst
new file mode 100644 (file)
index 0000000..5ad8c6d
--- /dev/null
@@ -0,0 +1,3 @@
+Ensure, if ``wraps`` is supplied to :class:`unittest.mock.MagicMock`, it is used
+to calculate return values for the magic methods instead of using the default
+return values. Patch by Karthikeyan Singaravelan.