Assert a function/method was not called using Mock
Asked Answered
E

7

187

I'm using the Mock library to test my application, but I want to assert that some function was not called. Mock docs talk about methods like mock.assert_called_with and mock.assert_called_once_with, but I didn't find anything like mock.assert_not_called or something related to verify mock was NOT called.

I could go with something like the following, though it doesn't seem cool nor pythonic:

def test_something:
    # some actions
    with patch('something') as my_var:
        try:
            # args are not important. func should never be called in this test
            my_var.assert_called_with(some, args)
        except AssertionError:
            pass  # this error being raised means it's ok
    # other stuff

Any ideas how to accomplish this?

Elegiac answered 29/8, 2012 at 21:59 Comment(1)
As @Ahmet points out in his answer, assert_not_called is now supported, also in the backport (docs.python.org/3/library/…).Backwardation
S
183

This should work for your case;

assert not my_var.called, 'method should not have been called'

Sample;

>>> mock=Mock()
>>> mock.a()
<Mock name='mock.a()' id='4349129872'>
>>> assert not mock.b.called, 'b was called and should not have been'
>>> assert not mock.a.called, 'a was called and should not have been'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError: a was called and should not have been
Sherburne answered 29/8, 2012 at 22:12 Comment(5)
Does this answer require Django? I'm getting an error: AttributeError: MockCallable instance has no attribute 'called'Rutherford
@NathanArthur Hm, I don't think so, after sudo easy_install -U mock and from mock import Mock on MacOS, the above runs without a hitch. Never installed Django :)Sherburne
Hmm. That's odd. I'm running Python 2.7.1 and am using unittest and from mock import Mock with Python Mock 0.1.0 for my tests. Does any of that sound problematic?Rutherford
I'm mocking a callable class from another module, so it looks like module_to_test.another_module.class = mock.Mock(), can you confirm this doesn't remember calls across different test cases (unittest.TestCase instances)? I think the call count doesn't reset in this casePublish
Use assert_not_called methodDisciplinarian
I
113

Though an old question, I would like to add that currently mock library (backport of unittest.mock) supports assert_not_called method.

Just upgrade yours;

pip install mock --upgrade

Indigotin answered 10/1, 2017 at 12:12 Comment(0)
C
48

With python >= 3.5 you can use mock_object.assert_not_called().

Cyn answered 7/12, 2018 at 13:43 Comment(0)
M
32

You can check the called attribute, but if your assertion fails, the next thing you'll want to know is something about the unexpected call, so you may as well arrange for that information to be displayed from the start. Using unittest, you can check the contents of call_args_list instead:

self.assertItemsEqual(my_var.call_args_list, [])

When it fails, it gives a message like this:

AssertionError: Element counts were not equal:
First has 0, Second has 1:  call('first argument', 4)
Misconduct answered 2/7, 2013 at 17:48 Comment(0)
Q
20

When you test using class inherits unittest.TestCase you can simply use methods like:

  • assertTrue
  • assertFalse
  • assertEqual

and similar (in python documentation you find the rest).

In your example we can simply assert if mock_method.called property is False, which means that method was not called.

import unittest
from unittest import mock

import my_module

class A(unittest.TestCase):
    def setUp(self):
        self.message = "Method should not be called. Called {times} times!"

    @mock.patch("my_module.method_to_mock")
    def test(self, mock_method):
        my_module.method_to_mock()

        self.assertFalse(mock_method.called,
                         self.message.format(times=mock_method.call_count))
Quarantine answered 24/6, 2015 at 21:48 Comment(1)
I cannot understand why this answer has so few votes.... to me, this is the right answer!Russia
A
2

Judging from other answers, no one except @rob-kennedy talked about the call_args_list.

It's a powerful tool for that you can implement the exact contrary of MagicMock.assert_called_with()

call_args_list is a list of call objects. Each call object represents a call made on a mocked callable.

>>> from unittest.mock import MagicMock
>>> m = MagicMock()
>>> m.call_args_list
[]
>>> m(42)
<MagicMock name='mock()' id='139675158423872'>
>>> m.call_args_list
[call(42)]
>>> m(42, 30)
<MagicMock name='mock()' id='139675158423872'>
>>> m.call_args_list
[call(42), call(42, 30)]

Consuming a call object is easy, since you can compare it with a tuple of length 2 where the first component is a tuple containing all the positional arguments of the related call, while the second component is a dictionary of the keyword arguments.

>>> ((42,),) in m.call_args_list
True
>>> m(42, foo='bar')
<MagicMock name='mock()' id='139675158423872'>
>>> ((42,), {'foo': 'bar'}) in m.call_args_list
True
>>> m(foo='bar')
<MagicMock name='mock()' id='139675158423872'>
>>> ((), {'foo': 'bar'}) in m.call_args_list
True

So, a way to address the specific problem of the OP is

def test_something():
    with patch('something') as my_var:
        assert ((some, args),) not in my_var.call_args_list

Note that this way, instead of just checking if a mocked callable has been called, via MagicMock.called, you can now check if it has been called with a specific set of arguments.

That's useful. Say you want to test a function that takes a list and call another function, compute(), for each of the value of the list only if they satisfy a specific condition.

You can now mock compute, and test if it has been called on some value but not on others.

Adolphus answered 24/9, 2016 at 16:54 Comment(0)
N
-1

My preferred idiom for checking that a mock was called/not called is as follows: self.assertEqual(post_mock.call_count, 0)

Benefits:

  1. Making sure that the mock was called/not called the correct number of times.
  2. Using the internal PyTest assertEqual function.
Nadanadab answered 8/9, 2022 at 9:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.