Karthikeyan Singaravelan <tir.kar...@gmail.com> added the comment:

@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:

----------
nosy: +cjw296, mariocj89
versions: +Python 3.7, Python 3.8 -Python 3.6

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue26752>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to