bpo-36871: Ensure method signature is used when asserting mock calls to a method (GH13261)

* Fix call_matcher for mock when using methods

* Add NEWS entry

* Use None check and convert doctest to unittest

* Use better name for mock in tests. Handle _SpecState when the attribute was not accessed and add tests.

* Use reset_mock instead of reinitialization. Change inner class constructor signature for check

* Reword comment regarding call object lookup logic
This commit is contained in:
Xtreak 2019-08-29 11:39:01 +05:30 committed by Chris Withers
parent 03acba6f1a
commit c96127821e
3 changed files with 86 additions and 1 deletions

View file

@ -804,6 +804,35 @@ class NonCallableMock(Base):
return message % (action, expected_string, actual_string)
def _get_call_signature_from_name(self, name):
"""
* If call objects are asserted against a method/function like obj.meth1
then there could be no name for the call object to lookup. Hence just
return the spec_signature of the method/function being asserted against.
* If the name is not empty then remove () and split by '.' to get
list of names to iterate through the children until a potential
match is found. A child mock is created only during attribute access
so if we get a _SpecState then no attributes of the spec were accessed
and can be safely exited.
"""
if not name:
return self._spec_signature
sig = None
names = name.replace('()', '').split('.')
children = self._mock_children
for name in names:
child = children.get(name)
if child is None or isinstance(child, _SpecState):
break
else:
children = child._mock_children
sig = child._spec_signature
return sig
def _call_matcher(self, _call):
"""
Given a call (or simply an (args, kwargs) tuple), return a
@ -811,7 +840,12 @@ class NonCallableMock(Base):
This is a best effort method which relies on the spec's signature,
if available, or falls back on the arguments themselves.
"""
sig = self._spec_signature
if isinstance(_call, tuple) and len(_call) > 2:
sig = self._get_call_signature_from_name(_call[0])
else:
sig = self._spec_signature
if sig is not None:
if len(_call) == 2:
name = ''