Skip to content

Commit

Permalink
bpo-38473: Handle autospecced functions and methods used with attach_…
Browse files Browse the repository at this point in the history
…mock (GH-16784) (#18166)

If an autospecced object is attached using attach_mock the
child would be a function with mock object as attribute from
which signature has to be derived.

(cherry picked from commit 66b00a9)

Co-authored-by: Karthikeyan Singaravelan <tir.karthi@gmail.com>
  • Loading branch information
miss-islington and tirkarthi committed Jan 25, 2020
1 parent fd9ce2b commit 71d2b33
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Lib/unittest/mock.py
Expand Up @@ -794,6 +794,10 @@ def _get_call_signature_from_name(self, name):
if child is None or isinstance(child, _SpecState):
break
else:
# If an autospecced object is attached using attach_mock the
# child would be a function with mock object as attribute from
# which signature has to be derived.
child = _extract_mock(child)
children = child._mock_children
sig = child._spec_signature

Expand Down
29 changes: 29 additions & 0 deletions Lib/unittest/test/testmock/testmock.py
Expand Up @@ -1863,6 +1863,35 @@ def test_attach_mock_patch_autospec(self):
self.assertEqual(mock_func.mock._extract_mock_name(), 'mock.child')


def test_attach_mock_patch_autospec_signature(self):
with mock.patch(f'{__name__}.Something.meth', autospec=True) as mocked:
manager = Mock()
manager.attach_mock(mocked, 'attach_meth')
obj = Something()
obj.meth(1, 2, 3, d=4)
manager.assert_has_calls([call.attach_meth(mock.ANY, 1, 2, 3, d=4)])
obj.meth.assert_has_calls([call(mock.ANY, 1, 2, 3, d=4)])
mocked.assert_has_calls([call(mock.ANY, 1, 2, 3, d=4)])

with mock.patch(f'{__name__}.something', autospec=True) as mocked:
manager = Mock()
manager.attach_mock(mocked, 'attach_func')
something(1)
manager.assert_has_calls([call.attach_func(1)])
something.assert_has_calls([call(1)])
mocked.assert_has_calls([call(1)])

with mock.patch(f'{__name__}.Something', autospec=True) as mocked:
manager = Mock()
manager.attach_mock(mocked, 'attach_obj')
obj = Something()
obj.meth(1, 2, 3, d=4)
manager.assert_has_calls([call.attach_obj(),
call.attach_obj().meth(1, 2, 3, d=4)])
obj.meth.assert_has_calls([call(1, 2, 3, d=4)])
mocked.assert_has_calls([call(), call().meth(1, 2, 3, d=4)])


def test_attribute_deletion(self):
for mock in (Mock(), MagicMock(), NonCallableMagicMock(),
NonCallableMock()):
Expand Down
@@ -0,0 +1,2 @@
Use signature from inner mock for autospecced methods attached with
:func:`unittest.mock.attach_mock`. Patch by Karthikeyan Singaravelan.

0 comments on commit 71d2b33

Please sign in to comment.