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.

classification
Title: Cryptic error message if incorrect spec is set on a callable mock
Type: behavior Stage:
Components: Library (Lib) Versions: Python 3.9, Python 3.8, Python 3.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: cjw296, mariocj89, michael.foord, uranusjr, xtreak
Priority: normal Keywords:

Created on 2015-10-04 18:07 by uranusjr, last changed 2022-04-11 14:58 by admin.

Messages (2)
msg252276 - (view) Author: Tzu-ping Chung (uranusjr) * Date: 2015-10-04 18:07
>>> from unittest import mock
>>> 
>>> class Foo:
...     def __init__(self, val):
...         pass
...     def func(self):
...         pass
... 
>>> class FooMock(mock.Mock):
...     def _get_child_mock(self, **kwargs):
...         return mock.Mock(spec_set=Foo)
... 
>>> mock_foo = FooMock()
>>> mock_foo.func()
<Mock name='mock()' id='4340652632'>
>>> mock_foo.func.mock_calls
[call()]
>>> mock_foo.func.assert_has_calls([mock.call()])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.5/unittest/mock.py", line 824, in assert_has_calls
    ) from cause
AssertionError: Calls not found.
Expected: [call()]
Actual: [call()]


While this code is expected to fail (FooMock.func is set to an incorrect spec), the error message is misleading and does not make any sense.
msg348840 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python committer) Date: 2019-08-01 05:43
Using spec only checks for the attribute to be present and not the signature of the actual attribute being called. You might need autospec for this behavior. The below example to use create_autospec does the correct validation for mock_foo.func. There is an open issue to make spec also do signature validation but it could go into 3.9 only if accepted since it changes semantics : issue30587. Regarding the error message being cryptic there are other issues open on assertion error being displayed with actual and expected call list being same. I would propose closing this issue. Maybe the docs could be updated about this behavior to make sure using spec is more clear on the expected behavior.

https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock

spec: This can be either a list of strings or an existing object (a class or instance) that acts as the specification for the mock object. If you pass in an object then a list of strings is formed by calling dir on the object (excluding unsupported magic attributes and methods). Accessing any attribute not in this list will raise an AttributeError.

# bpo25312.py

from unittest.mock import call, create_autospec, Mock
import inspect

class Foo:

    def __init__(self, val):
        pass

    def func(self):
        pass

class FooMock(Mock):

    def _get_child_mock(self, **kwargs):
        return create_autospec(Foo)

mock_foo = FooMock()
print(inspect.signature(mock_foo.func)) # Signature is correct with create_autospec whereas with Mock(spec=Foo) it is (*args, **kwargs)
mock_foo.func() # Raises TypeError with (val) being signature.
mock_foo.func.assert_has_calls([call()])


➜  cpython git:(master) ✗ ./python.exe bpo25312.py
(val)
Traceback (most recent call last):
  File "/Users/karthikeyansingaravelan/stuff/python/cpython/bpo25312.py", line 19, in <module>
    mock_foo.func()
  File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 1027, in __call__
    self._mock_check_sig(*args, **kwargs)
  File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 119, in checksig
    sig.bind(*args, **kwargs)
  File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/inspect.py", line 3025, in bind
    return self._bind(args, kwargs)
  File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/inspect.py", line 2940, in _bind
    raise TypeError(msg) from None
TypeError: missing a required argument: 'val'
History
Date User Action Args
2022-04-11 14:58:22adminsetgithub: 69499
2019-08-01 05:43:20xtreaksetnosy: + cjw296, mariocj89

messages: + msg348840
versions: + Python 3.7, Python 3.8, Python 3.9, - Python 3.4, Python 3.5, Python 3.6
2018-09-21 12:47:23xtreaksetnosy: + xtreak
2015-10-09 18:44:01terry.reedysetnosy: + michael.foord
2015-10-04 18:07:08uranusjrcreate