[issue27715] call-matcher breaks if a method is mocked with spec=True

Karthikeyan Singaravelan report at bugs.python.org
Fri Dec 14 13:11:33 EST 2018


Karthikeyan Singaravelan <tir.karthi at gmail.com> added the comment:

I think the original issue with patch.object reported by Carl is different from the one reported by David for autospec. Analyzed the report by David and When we call autospec on a class with instance=True then the spec is modeled on the signature of __call__ instead of __init__ where __call__ has the signature of (self, x) and self is  discarded with _eat_self passed as True. But mock also stores _spec_signature that is not aware of skipping self and has the signature as (self, x) and is used for checking signature in mock.assert_called_with. When instance=True then kwargs['_spec_as_instance']=True so does it makes sense to set kwargs['_eat_self'] = True at [0] ? I applied the change and there are no test failures so this deserves a test.

This makes __call__ to have a different signature when it's called from mock and when the call is checked with assert_called_with_once. But it's not the case for methods of the class where self is skipped both for mock and _spec_signature. Since __signature__ is set for mock with my PR this makes it little easy to debug. Can this be dealt as a separate issue?

I would also love to see if the assertion message can be improved since expected_call and actual_call are printed with repr version of the call object in spite of the signature failure. This has caused confusion here and in other places like issue26752 and issue25312.

Sample program to demonstrate difference in signatures :  

import inspect
from unittest import mock

class Foo:

    def __call__(self, x):
        return x

    def bar(self, x):
        pass

m = mock.create_autospec(Foo, instance=True)
m(7)
m.bar(7)
print(inspect.signature(m))
print(m._spec_signature)
print(inspect.signature(m.bar))
print(m.bar._spec_signature)
m.bar.assert_called_once_with(7) # 7 passed as self with no value for x
m.assert_called_once_with(7) # Fails due to self

(x)
(self, x) # self is not skipped in _spec_signature
(x)
(x)
TypeError: missing a required argument: 'x'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "../backups/bpo27715_1.py", line 20, in <module>
    m.assert_called_once_with(7)
  File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 840, in assert_called_once_with
    return self.assert_called_with(*args, **kwargs)
  File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 827, in assert_called_with
    raise AssertionError(_error_message()) from cause
AssertionError: Expected call: mock(7)
Actual call: mock(7)


[0] https://github.com/python/cpython/blob/f8e9bd568adf85c1e4aea1dda542a96b027797e2/Lib/unittest/mock.py#L2199

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

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue27715>
_______________________________________


More information about the Python-bugs-list mailing list