Andrey Semakin's answer should be the accepted one. The accepted answer is problematic because assert_called_with
only examines the last call of the mocked funtion. Supposing you have 6 calls: you may want to find out whether a given call with given parameters was or was not made as any (or more than one) of those 6 calls. The accepted answer doesn't work for that case, but Andrey Semakin's does.
In fact you could also accomplish what his solution does using assert_any_call
and catching the assert exception.
Andrey's answer can be made even more useful, though: supposing you don't want to stipulate all the args
or all the kwargs
? With his solution (and assert_any_call
exception-catching) you have to stipulate precisely ALL the args
and all the kwargs
, or a match doesn't happen. If you're looking for the absence of a match, equally, having to stipulate ALL args and ALL kwargs may typically be too demanding a requirement in a testing context.
case 1: disregarding the args
This can be accomplished pretty easily by using a dummy class:
class AnyArg(object):
def __eq__(self, b):
return True
args = (AnyArg(),)
kwargs = {'command': 'put'}
assert mock.call(*args, **kwargs) in mock_process.mock_calls
NB if your args
is a mix of real args and AnyArg
, I'm not sure what would happen and haven't experimented to find out. Really this solution is simply for the purpose of disregarding ALL args. But it is still problematic: any other supplied kwargs
in a call and the match fails (or you get what might be a false negative if you're checking on absence).
case 2: disregarding the kwargs (or both args and kwargs)
This is more difficult. You can't fake the finding or not finding of key-value pairs in another list of key-value pairs. I think this is about as compact as possible:
def is_match_found(mock_func, required_args=(), required_kwargs={}):
for args_list in mock_func.call_args_list:
for required_arg in required_args:
if required_arg not in args_list.args:
break # go on to next call
else:
for required_key in required_kwargs:
if required_key not in args_list.kwargs:
break # go on to next call
else:
if required_kwargs[required_key] != args_list.kwargs[required_key]:
# values differ
break # go on to next call
else:
return True
return False
examples of use:
# stipulating some args, disregarding kwargs
assert is_match_found(mock_process, ('some parameter', 33,))
# stipulating some args and some kwargs, "permissively"
assert not is_match_found(mock_process, ('some parameter', 33,), {'command': 'put',})
# stipulating some args and some kwargs, disregarding *values* of named params,
# but ensuring that those named parameters are present
assert not is_match_found(mock_process, ('some parameter', 33,), {'command': AnyArg(),})
I use the word "permissively", meaning that any call to this function, as written, disregards completely any extra args or kwargs which DON'T match. Obviously, infinite refinements could be made to the above (in particular the order of the args
might be a requirement), but for testing purposes I think this is quite a useful tool as written.
respecting order of required args
This'll hopefully do the trick (and hopefully give correct results with repeated args):
def is_match_found(mock_func, required_args=(), required_kwargs={}, args_must_be_leading=True):
for args_list in mock_func.call_args_list:
call_args = args_list.args
for required_arg in required_args:
if required_arg not in call_args:
break # go on to next call
index_in_call_args = call_args.index(required_arg)
if args_must_be_leading and index_in_call_args != 0:
break # go on to next call
call_args = call_args[index_in_call_args + 1:]
[snip...]
Unfortunately, it turns out you can't assign to args_list.args
, so I thought initially that we'd be forced to make a (potentially expensive deep) copy of that list. However it seems that just assigning call_args
to args_list.args
lets you circumvent the problem.
This is far from perfect, of course. In particular, in the above first example, if args_must_be_leading == False
a call with args
(None, 12, 'some parameter', 3, 3, 33, 'entropy')
would qualify as a "pass". I added that param to achieve a slightly more sensible balance between permissiveness and restriction.
constructor: unittest.mock.call(*args, **kwargs)
, example:call(1, 2, a='foo', b='bar')
. To detect absence of a call you could also useassert_any_call(...)
and catch exceptions. Only problem is, with both solutions, you have to include ALL args and ALL kwargs for the correct result. Supposing you just want to test all args, ignoring kwargs ... or just one arg of the args... See my answer for a solution. – Rhizocarpous