From 2acd689048c86647734bee0fa5a3c2f43aa88e53 Mon Sep 17 00:00:00 2001 From: infohash <46137868+infohash@users.noreply.github.com> Date: Tue, 23 Apr 2024 02:21:56 +0530 Subject: [PATCH 1/3] gh-90848: Fixed create_autospec ignoring configure_mock style kwargs --- Lib/test/test_unittest/testmock/testmock.py | 20 ++++++++++++++++++++ Lib/unittest/mock.py | 20 ++++++++++++-------- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_unittest/testmock/testmock.py b/Lib/test/test_unittest/testmock/testmock.py index b81b3049d56bf8..0ac51e5d525fbc 100644 --- a/Lib/test/test_unittest/testmock/testmock.py +++ b/Lib/test/test_unittest/testmock/testmock.py @@ -115,6 +115,26 @@ def f(): pass with self.assertRaises(TypeError): mock() + def test_create_autospec_should_be_configurable_by_kwargs(self): + """If kwargs are given to set side_effect & return_value, the function + must configure the parent mock during initialization.""" + class Result: + def get_result(self): + pass + + mocked_result = 'mocked value' + class_mock = create_autospec(spec=Result, **{ + # Test both side_effect & return_value which is why the 2nd + # parameter of side_effect is DEFAULT. + 'return_value.get_result.side_effect': [ValueError, DEFAULT], + 'return_value.get_result.return_value': mocked_result}) + with self.assertRaises(ValueError): + class_mock().get_result() + # Call it again to test return_value. + self.assertEqual(class_mock().get_result(), mocked_result) + # Only the parent mock should be configurable because the user will + # pass kwargs with respect to the parent mock. + self.assertEqual(class_mock().return_value.get_result.side_effect, None) def test_repr(self): mock = Mock(name='foo') diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 1799e9bbf58592..5fbed94e58014d 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -2788,8 +2788,8 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None, if _parent is not None and not instance: _parent._mock_children[_name] = mock - wrapped = kwargs.get('wraps') - + # Pop "wraps" from kwargs because it will not be used again. + wrapped = kwargs.pop('wraps', None) if is_type and not instance and 'return_value' not in kwargs: mock.return_value = create_autospec(spec, spec_set, instance=True, _name='()', _parent=mock, @@ -2814,12 +2814,12 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None, except AttributeError: continue - kwargs = {'spec': original} + child_kwargs = {'spec': original} # Wrap child attributes also. if wrapped and hasattr(wrapped, entry): - kwargs.update(wraps=original) + child_kwargs.update(wraps=original) if spec_set: - kwargs = {'spec_set': original} + child_kwargs = {'spec_set': original} if not isinstance(original, FunctionTypes): new = _SpecState(original, spec_set, mock, entry, instance) @@ -2830,14 +2830,13 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None, parent = mock.mock skipfirst = _must_skip(spec, entry, is_type) - kwargs['_eat_self'] = skipfirst + child_kwargs['_eat_self'] = skipfirst if iscoroutinefunction(original): child_klass = AsyncMock else: child_klass = MagicMock new = child_klass(parent=parent, name=entry, _new_name=entry, - _new_parent=parent, - **kwargs) + _new_parent=parent, **child_kwargs) mock._mock_children[entry] = new new.return_value = child_klass() _check_signature(original, new, skipfirst=skipfirst) @@ -2848,6 +2847,11 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None, # setting as an instance attribute? if isinstance(new, FunctionTypes): setattr(mock, entry, new) + # kwargs are passed with respect to the parent mock so, they are not used + # for creating return_value of the parent mock. So, this condition + # should be true only for the parent mock if kwargs are given. + if _is_instance_mock(mock) and kwargs: + mock.configure_mock(**kwargs) return mock From 7c4107be8f482f97eee3086a53c9d06e59a64b92 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 21:54:13 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2024-04-22-21-54-12.gh-issue-90848.5jHEEc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2024-04-22-21-54-12.gh-issue-90848.5jHEEc.rst diff --git a/Misc/NEWS.d/next/Library/2024-04-22-21-54-12.gh-issue-90848.5jHEEc.rst b/Misc/NEWS.d/next/Library/2024-04-22-21-54-12.gh-issue-90848.5jHEEc.rst new file mode 100644 index 00000000000000..adbca0127ed0ce --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-22-21-54-12.gh-issue-90848.5jHEEc.rst @@ -0,0 +1 @@ +Fixed :func:`unittest.mock.create_autospec` to configure parent mock with keyword arguments. From a2cb04d7685d08d87be35e96a9ac3268ff095c61 Mon Sep 17 00:00:00 2001 From: infohash <46137868+infohash@users.noreply.github.com> Date: Tue, 23 Apr 2024 13:03:28 +0530 Subject: [PATCH 3/3] fixed comments --- Lib/test/test_unittest/testmock/testmock.py | 23 +++++++-------------- Lib/unittest/mock.py | 2 +- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/Lib/test/test_unittest/testmock/testmock.py b/Lib/test/test_unittest/testmock/testmock.py index 0ac51e5d525fbc..77f6f1eb4b76b9 100644 --- a/Lib/test/test_unittest/testmock/testmock.py +++ b/Lib/test/test_unittest/testmock/testmock.py @@ -116,25 +116,18 @@ def f(): pass mock() def test_create_autospec_should_be_configurable_by_kwargs(self): - """If kwargs are given to set side_effect & return_value, the function - must configure the parent mock during initialization.""" - class Result: - def get_result(self): - pass - + """If kwargs are given to configure mock, the function must configure + the parent mock during initialization.""" mocked_result = 'mocked value' - class_mock = create_autospec(spec=Result, **{ - # Test both side_effect & return_value which is why the 2nd - # parameter of side_effect is DEFAULT. - 'return_value.get_result.side_effect': [ValueError, DEFAULT], - 'return_value.get_result.return_value': mocked_result}) + class_mock = create_autospec(spec=Something, **{ + 'return_value.meth.side_effect': [ValueError, DEFAULT], + 'return_value.meth.return_value': mocked_result}) with self.assertRaises(ValueError): - class_mock().get_result() - # Call it again to test return_value. - self.assertEqual(class_mock().get_result(), mocked_result) + class_mock().meth(a=None, b=None, c=None) + self.assertEqual(class_mock().meth(a=None, b=None, c=None), mocked_result) # Only the parent mock should be configurable because the user will # pass kwargs with respect to the parent mock. - self.assertEqual(class_mock().return_value.get_result.side_effect, None) + self.assertEqual(class_mock().return_value.meth.side_effect, None) def test_repr(self): mock = Mock(name='foo') diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 5fbed94e58014d..a2634b6164062a 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -2788,7 +2788,7 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None, if _parent is not None and not instance: _parent._mock_children[_name] = mock - # Pop "wraps" from kwargs because it will not be used again. + # Pop wraps from kwargs because it must not be passed to configure_mock. wrapped = kwargs.pop('wraps', None) if is_type and not instance and 'return_value' not in kwargs: mock.return_value = create_autospec(spec, spec_set, instance=True,