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.