问题
When a unittest.mock.Mock
object has been called, I can check for the argument values with the exact signature of the call:
from unittest.mock import Mock
m = Mock() # creation of mock
m('foo', bar='baz') # call to the mock
m.assert_called_once_with('foo', bar='baz') # check call arguments
Checking for a different signature with the same values will fail. E.g., if we check with 'baz'
as a positional argument instead of a named argument, the assertion will fail:
m.assert_called_once_with('foo', 'baz')
# AssertionError: Expected call: mock('foo', 'baz')
# Actual call: mock('foo', bar='baz')
It has to. If the function replaced by m
was
def actual_fu(foo, bar):
# do something
then the calls would be equivalent, but if it was
def a_different_actual_fu(foo, *args, bar='some default'):
# do something
then the calls would not be equivalent. Mock
doesn't know the actual function's signature, so it can't rely on the equivalence we would have in the first case.
Is there a way of checking the call argument values that is agnostic about whether they were passed positionally or as keyword arguments, by letting the Mock (or an assertion helper function or similar) know about the actual function replaced by the mock?
The Mock
object can be made aware of the object it replaces (which can be a function or method) with the optional spec argument or with autospeccing, but those serve a different purpose (limiting what calls to allow on the mock) and don't affect after-the-fact checking.
回答1:
The Mock object can be made aware of the object it replaces (which can be a function or method) with the optional spec argument or with
autospeccing
, but those serve a different purpose..
This is exactly what Issue 17015: mock could be smarter and inspect the spec's signature improvement issue was about. The spec
actually is very much related and now makes the mock function-signature-aware.
See how mock fails when we assert mock was called with a keyword argument - without letting it know about the actual function signature:
>>> from unittest.mock import Mock
>>>
>>> def actual_fu(foo, bar):
... pass
>>>
>>> m = Mock()
>>> m('foo', bar='baz')
<Mock name='mock()' id='4356741496'>
>>>
>>> m.assert_called_once_with(foo='foo', bar='baz')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/unittest/mock.py", line 803, in assert_called_once_with
return self.assert_called_with(*args, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/unittest/mock.py", line 792, in assert_called_with
raise AssertionError(_error_message()) from cause
AssertionError: Expected call: mock(bar='baz', foo='foo')
Actual call: mock('foo', bar='baz')
And now, see how it all passes if we provide a spec
:
>>> m = Mock(spec=actual_fu)
>>> m('foo', bar='baz')
<Mock name='mock()' id='4356249528'>
>>>
>>> m.assert_called_once_with(foo='foo', bar='baz')
>>> m.assert_called_once_with('foo', bar='baz')
>>> m.assert_called_once_with(bar='baz', foo='foo')
>>> m.assert_called_once_with('foo', 'baz')
>>>
(Used Python 3.5.1)
来源:https://stackoverflow.com/questions/34945206/check-unittest-mock-call-arguments-agnostically-w-r-t-whether-they-have-been-pa