From af4026c23f74261c7cf8d095d14b18b81073bd1f Mon Sep 17 00:00:00 2001 From: Xtreak Date: Mon, 19 Aug 2019 22:27:50 +0530 Subject: [PATCH 1/4] Ensure wraps is called for magic methods in MagicMock. --- Lib/unittest/mock.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index a98decc2c9f008..c11a7e8b12b430 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1969,6 +1969,12 @@ def __aiter__(): def _set_return_value(mock, method, name): + # If _mock_wraps is present then attach it so that it's 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 From cac9f78755d10bbe469750c6d66779b26dd9f299 Mon Sep 17 00:00:00 2001 From: Xtreak Date: Thu, 12 Sep 2019 12:09:03 +0100 Subject: [PATCH 2/4] Ensure wraps is used for magic methods when passed. --- Lib/unittest/mock.py | 4 +-- Lib/unittest/test/testmock/testmock.py | 47 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index c11a7e8b12b430..7141043ead58b8 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1969,8 +1969,8 @@ def __aiter__(): def _set_return_value(mock, method, name): - # If _mock_wraps is present then attach it so that it's return - # value is used when called. + # 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 diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index 581afaaeb815df..f54e30efdffb9e 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -707,6 +707,53 @@ def method(self): pass 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) From 859d1b9ee9781b142e85938dbcb9a88245cf1cb7 Mon Sep 17 00:00:00 2001 From: Xtreak Date: Thu, 12 Sep 2019 12:11:14 +0100 Subject: [PATCH 3/4] Add NEWS entry. --- .../next/Library/2019-09-12-12-11-05.bpo-25597.mPMzVx.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2019-09-12-12-11-05.bpo-25597.mPMzVx.rst 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 index 00000000000000..7c6d64e497cca9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-09-12-12-11-05.bpo-25597.mPMzVx.rst @@ -0,0 +1,3 @@ +Ensure ``wraps`` is supplied to :class:`unittest.mock.MagicMock` is used to +calculate return values for the magic methods instead of using the default +return values. Patch by Karthikeyan Singaravelan. From a76e310e8cfceb9d7594021a165e880306c388bd Mon Sep 17 00:00:00 2001 From: Karthikeyan Singaravelan Date: Sun, 26 Jan 2020 22:11:29 +0530 Subject: [PATCH 4/4] Update news entry. --- .../next/Library/2019-09-12-12-11-05.bpo-25597.mPMzVx.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index 7c6d64e497cca9..5ad8c6d90fa033 100644 --- 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 @@ -1,3 +1,3 @@ -Ensure ``wraps`` is supplied to :class:`unittest.mock.MagicMock` is used to -calculate return values for the magic methods instead of using the default +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.