Consider the following files:
holy_hand_grenade.py
def count(one, two, five='three'):
print('boom')
test_holy_hand_grenade.py
from unittest import mock
import holy_hand_grenade
def test_hand_grenade():
mock_count = mock.patch("holy_hand_grenade.count", autospec=True)
with mock_count as fake_count:
fake_count(1, 2, five=5)
# According to https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.call_args
# this should work
assert fake_count.call_args.kwargs['five'] == 5
According to the docs, call_args
should be:
This is either None (if the mock hasn’t been called), or the arguments that the mock was last called with. This will be in the form of a tuple: the first member, which can also be accessed through the args property, is any ordered arguments the mock was called with (or an empty tuple) and the second member, which can also be accessed through the kwargs property, is any keyword arguments (or an empty dictionary).
(emphasis mine)
But this blows up in my face, with TypeError: tuple indices must be integers or slices, not str
Um. No?
The thing I really don't understand, is that if this is a call object, which it is, because
assert isinstance(fake_count.call_args, (mock._Call,))
passes, it's supposed to have kwargs and args. And it... well, it sort of does. But they appear to not actually be the correct thing:
assert isinstance(fake_count.call_args.kwargs, (mock._Call,)) #this works
assert isinstance(fake_count.call_args.kwargs, (dict,)) # doesn't work
What am I doing wrong here?
This is a feature introduced in Python 3.8 in this issue.
The 3.7 documentation does not mention it (while the newest docs do) - so you have to access the arguments by index in Python < 3.8.