This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author xtreak
Recipients cjw296, guboi72, jekin000, jwdevel, mariocj89, michael.foord, rbcollins, xtreak
Date 2019-03-01.10:20:28
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1551435629.2.0.612593790355.issue26752@roundup.psfhosted.org>
In-reply-to
Content
@guboi72 is right that signature for class constructor is used also for methods. Another possible solution would be that mock stores it's children in _mock_children dictionary so when a method is called then name can be used to get the relevant child mock which would have the signature set that can be used.

In the below program call().foo also uses the signature (a, b) of __init__ and in the call_matcher check name "foo" can be used to get the child mock from mock_class._mock_children that will have the signature set during create_autospec. This will give the signature (a) but it's little difficult to construct the name. It also needs to handle cases for inner classes like Foo.Bar.foo() where Bar is an inner class inside Foo.

from unittest.mock import *

class Foo:

    def __init__(self, a, b):
        pass

    def foo(self, a):
        pass


mock_class = create_autospec(Foo)
mock = mock_class(1, 2)
mock.foo(1)
print(mock_class._mock_children)
print(mock._mock_children)
mock_class.assert_has_calls([call(1, 2), call().foo(1)])
mock.assert_has_calls([call.foo(1)])


$ python3.7 ../backups/unittest_mock_spec_conflict.py
{'foo': <MagicMock name='mock.foo' spec='function' id='4534528096'>}
{'foo': <MagicMock name='mock().foo' spec='function' id='4534099584'>}
Traceback (most recent call last):
  File "../backups/unittest_mock_spec_conflict.py", line 17, in <module>
    mock_class.assert_has_calls([call(1, 2), call().foo(1)])
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/mock.py", line 852, in assert_has_calls
    ) from cause
AssertionError: Calls not found.
Expected: [call(1, 2), call().foo(1)]
Actual: [call(1, 2), call().foo(1)]

A very rough hack that fixes the above case and explains my approach but not so robust.

diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index 2ccf0d82ce..f0e917d57e 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -777,7 +777,17 @@ class NonCallableMock(Base):
             else:
                 name, args, kwargs = _call
             try:
-                return name, sig.bind(*args, **kwargs)
+                if name:
+                    if name.startswith("()"):
+                        mock_name = "mock" + name # Handle call().foo where name is ().foo
+                    else:
+                        mock_name = "mock." + name # Handle call.foo where name is foo
+                    sig = self._mock_children.get(mock_name)
+
+                if sig:
+                    return name, sig.bind(*args, **kwargs)
+                else:
+                    return _call
             except TypeError as e:
                 return e.with_traceback(None)
         else:
History
Date User Action Args
2019-03-01 10:20:29xtreaksetrecipients: + xtreak, rbcollins, cjw296, michael.foord, jekin000, jwdevel, mariocj89, guboi72
2019-03-01 10:20:29xtreaksetmessageid: <1551435629.2.0.612593790355.issue26752@roundup.psfhosted.org>
2019-03-01 10:20:29xtreaklinkissue26752 messages
2019-03-01 10:20:28xtreakcreate