mirror of
https://github.com/python/cpython.git
synced 2024-12-01 05:45:40 +08:00
bpo-21478: Record calls to parent when autospecced objects are used as child with attach_mock (GH 14688)
* Clear name and parent of mock in autospecced objects used with attach_mock * Add NEWS entry * Fix reversed order of comparison * Test child and standalone function calls * Use a helper function extracting mock to avoid code duplication and refactor tests.
This commit is contained in:
parent
b530a4460b
commit
7397cda997
@ -72,6 +72,15 @@ def _is_exception(obj):
|
||||
)
|
||||
|
||||
|
||||
def _extract_mock(obj):
|
||||
# Autospecced functions will return a FunctionType with "mock" attribute
|
||||
# which is the actual mock object that needs to be used.
|
||||
if isinstance(obj, FunctionTypes) and hasattr(obj, 'mock'):
|
||||
return obj.mock
|
||||
else:
|
||||
return obj
|
||||
|
||||
|
||||
def _get_signature_object(func, as_instance, eat_self):
|
||||
"""
|
||||
Given an arbitrary, possibly callable object, try to create a suitable
|
||||
@ -346,13 +355,7 @@ class _CallList(list):
|
||||
|
||||
|
||||
def _check_and_set_parent(parent, value, name, new_name):
|
||||
# function passed to create_autospec will have mock
|
||||
# attribute attached to which parent must be set
|
||||
if isinstance(value, FunctionTypes):
|
||||
try:
|
||||
value = value.mock
|
||||
except AttributeError:
|
||||
pass
|
||||
value = _extract_mock(value)
|
||||
|
||||
if not _is_instance_mock(value):
|
||||
return False
|
||||
@ -467,10 +470,12 @@ class NonCallableMock(Base):
|
||||
Attach a mock as an attribute of this one, replacing its name and
|
||||
parent. Calls to the attached mock will be recorded in the
|
||||
`method_calls` and `mock_calls` attributes of this one."""
|
||||
mock._mock_parent = None
|
||||
mock._mock_new_parent = None
|
||||
mock._mock_name = ''
|
||||
mock._mock_new_name = None
|
||||
inner_mock = _extract_mock(mock)
|
||||
|
||||
inner_mock._mock_parent = None
|
||||
inner_mock._mock_new_parent = None
|
||||
inner_mock._mock_name = ''
|
||||
inner_mock._mock_new_name = None
|
||||
|
||||
setattr(self, attribute, mock)
|
||||
|
||||
|
@ -37,6 +37,9 @@ class Something(object):
|
||||
def smeth(a, b, c, d=None): pass
|
||||
|
||||
|
||||
def something(a): pass
|
||||
|
||||
|
||||
class MockTest(unittest.TestCase):
|
||||
|
||||
def test_all(self):
|
||||
@ -1808,6 +1811,26 @@ class MockTest(unittest.TestCase):
|
||||
self.assertEqual(m.mock_calls, call().foo().call_list())
|
||||
|
||||
|
||||
def test_attach_mock_patch_autospec(self):
|
||||
parent = Mock()
|
||||
|
||||
with mock.patch(f'{__name__}.something', autospec=True) as mock_func:
|
||||
self.assertEqual(mock_func.mock._extract_mock_name(), 'something')
|
||||
parent.attach_mock(mock_func, 'child')
|
||||
parent.child(1)
|
||||
something(2)
|
||||
mock_func(3)
|
||||
|
||||
parent_calls = [call.child(1), call.child(2), call.child(3)]
|
||||
child_calls = [call(1), call(2), call(3)]
|
||||
self.assertEqual(parent.mock_calls, parent_calls)
|
||||
self.assertEqual(parent.child.mock_calls, child_calls)
|
||||
self.assertEqual(something.mock_calls, child_calls)
|
||||
self.assertEqual(mock_func.mock_calls, child_calls)
|
||||
self.assertIn('mock.child', repr(parent.child.mock))
|
||||
self.assertEqual(mock_func.mock._extract_mock_name(), 'mock.child')
|
||||
|
||||
|
||||
def test_attribute_deletion(self):
|
||||
for mock in (Mock(), MagicMock(), NonCallableMagicMock(),
|
||||
NonCallableMock()):
|
||||
@ -1891,6 +1914,20 @@ class MockTest(unittest.TestCase):
|
||||
|
||||
self.assertRaises(TypeError, mock.child, 1)
|
||||
self.assertEqual(mock.mock_calls, [call.child(1, 2)])
|
||||
self.assertIn('mock.child', repr(mock.child.mock))
|
||||
|
||||
def test_parent_propagation_with_autospec_attach_mock(self):
|
||||
|
||||
def foo(a, b): pass
|
||||
|
||||
parent = Mock()
|
||||
parent.attach_mock(create_autospec(foo, name='bar'), 'child')
|
||||
parent.child(1, 2)
|
||||
|
||||
self.assertRaises(TypeError, parent.child, 1)
|
||||
self.assertEqual(parent.child.mock_calls, [call.child(1, 2)])
|
||||
self.assertIn('mock.child', repr(parent.child.mock))
|
||||
|
||||
|
||||
def test_isinstance_under_settrace(self):
|
||||
# bpo-36593 : __class__ is not set for a class that has __class__
|
||||
|
@ -0,0 +1,2 @@
|
||||
Record calls to parent when autospecced object is attached to a mock using
|
||||
:func:`unittest.mock.attach_mock`. Patch by Karthikeyan Singaravelan.
|
Loading…
Reference in New Issue
Block a user