summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/unittest/mock.py27
-rw-r--r--Lib/unittest/test/testmock/testmock.py37
-rw-r--r--Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst2
3 files changed, 55 insertions, 11 deletions
diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index c280272..b3dc640 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -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)
diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py
index 0f30bcc..090da45 100644
--- a/Lib/unittest/test/testmock/testmock.py
+++ b/Lib/unittest/test/testmock/testmock.py
@@ -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__
diff --git a/Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst b/Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst
new file mode 100644
index 0000000..0ac9b8e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-07-10-23-07-11.bpo-21478.cCw9rF.rst
@@ -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.