31

unittest.mock objects have an assert_not_called method available, but what I'm looking for is an assert_not_called_with. Is there anything like that? I looked on Google and didn't see anything, and when I tried just using mock_function.assert_not_called_with(...) it raised an AttributeError, meaning the function does not exist with that name.

My current solution:

with self.assertRaises(AssertionError): mock_function.assert_called_with(arguments_I_want_to_test) 

This works but clutters the code if I have several such calls I want to make.

Related:

5 Answers 5

31

You can add a assert_not_called_with method to unittest.mock.Mock on your own:

from unittest.mock import Mock def assert_not_called_with(self, *args, **kwargs): try: self.assert_called_with(*args, **kwargs) except AssertionError: return raise AssertionError('Expected %s to not have been called.' % self._format_mock_call_signature(args, kwargs)) Mock.assert_not_called_with = assert_not_called_with 

so that:

m = Mock() m.assert_not_called_with(1, 2, a=3) m(3, 4, b=5) m.assert_not_called_with(3, 4, b=5) 

outputs:

AssertionError: Expected mock(3, 4, b=5) to not have been called. 
Sign up to request clarification or add additional context in comments.

4 Comments

Instance of 'ClassA' has no 'assert_called_with' member - How to solve this problem?
This doesn't work. assert_called_with actually works on the LAST call to the mock. Supposing you have 6 calls to the mock and you want to check that no call with given args and kwargs occurred in any of those 6 calls. This won't work. Andrey Semakin's answer will. There is also assert_any_call which, as the name suggests, looks through all calls. But there is currently no assert_any_call_with.
@mikerodent My answer was meant for the OP's requirement in the question. The OP specifically says that assert_called_with already "works" when used with assertRaises for his/her intended purpose, so my answer is simply turning what "works" for the OP into a function. You are basically modifying the question with a different logical requirement. Although the OP now accepts Andrey's solution instead, it does not mean my answer "doesn't work" for the question's literal requirement.
@blhsing Yep, I accept that my answer goes way beyond what was asked but I'm not sure I totally agree about your answer: basically the OP seems pretty clearly to have believed (wrongly) that his proposed solution was indeed equivalent to assert_not_called as (potentially) applicable to a multiple-calls situation. It's a minor thing but I think some acknowledgement that assert_called_with only covers the last call to the mocked function should have been made.
25

One more solution that uses mock calls history:

from unittest.mock import call assert call(arguments_I_want_to_test) not in mock_function.mock_calls 

1 Comment

From the docs: constructor: unittest.mock.call(*args, **kwargs), example: call(1, 2, a='foo', b='bar'). To detect absence of a call you could also use assert_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.
6

Using Pytest, I assert that "AssertionError" is called:

import pytest from unittest.mock import Mock def test_something(): something.foo = Mock() # Test that something.foo(bar) is not called. with pytest.raises(AssertionError): something.foo.assert_called_with(bar) 

Comments

3

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.

Comments

1

Another option:

self.assertNotIn(argument_you_want_to_test, mock_function.call_args.kwargs) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.