【问题标题】:Assert a function/method was not called using Mock断言未使用 Mock 调用函数/方法
【发布时间】:2012-08-24 14:32:10
【问题描述】:

我正在使用 Mock 库来测试我的应用程序,但我想断言某些函数没有被调用。 Mock 文档讨论了 mock.assert_called_withmock.assert_called_once_with 之类的方法,但我没有找到类似 mock.assert_not_called 或与验证 mock 是否未调用相关的任何内容。

我可以使用类似以下的内容,尽管它看起来既不酷也不像 Python:

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

任何想法如何做到这一点?

【问题讨论】:

标签: python unit-testing mocking python-mock


【解决方案1】:

这应该适用于您的情况;

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

样品;

>>> 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

【讨论】:

  • 这个答案需要 Django 吗?我收到一个错误:AttributeError: MockCallable instance has no attribute 'called'
  • @NathanArthur 嗯,我不这么认为,在 MacOS 上的 sudo easy_install -U mockfrom mock import Mock 之后,上述运行顺利。从未安装过 Django :)
  • 嗯。这很奇怪。我正在运行 Python 2.7.1,并使用 unittest 和 from mock import Mock 和 Python Mock 0.1.0 进行测试。这听起来有问题吗?
  • 我正在模拟来自另一个模块的可调用类,所以它看起来像module_to_test.another_module.class = mock.Mock(),你能确认这不记得跨不同测试用例(unittest.TestCase 实例)的调用吗?我认为在这种情况下呼叫计数不会重置
  • 使用 assert_not_call 方法
【解决方案2】:

虽然是一个老问题,但我想补充一点,目前mock 库(unittest.mock 的反向移植)支持assert_not_called 方法。

只需升级你的;

pip install mock --upgrade

【讨论】:

    【解决方案3】:

    您可以检查called 属性,但如果您的断言失败,接下来您会想知道关于意外调用的信息,因此您不妨安排这些信息从头开始显示。使用unittest,可以改为查看call_args_list的内容:

    self.assertItemsEqual(my_var.call_args_list, [])
    

    当它失败时,它会给出这样的消息:

    AssertionError:元素计数不相等: First 有 0,Second 有 1: call('first argument', 4)

    【讨论】:

      【解决方案4】:

      使用python &gt;= 3.5,您可以使用mock_object.assert_not_called()

      【讨论】:

        【解决方案5】:

        当您使用类继承 unittest.TestCase 进行测试时,您可以简单地使用如下方法:

        • 断言真
        • assertFalse
        • assertEqual

        和类似的(在python documentation 你可以找到其余的)。

        在您的示例中,我们可以简单地断言 mock_method.call 属性是否为 False,这意味着该方法未被调用。

        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))
        

        【讨论】:

        • 我不明白为什么这个答案的票数这么少......对我来说,这是正确的答案!
        • 感谢@ClintEastwood,希望对您有所帮助! :)
        【解决方案6】:

        从其他答案来看,除了@rob-kennedy之外,没有人谈论过call_args_list

        这是一个强大的工具,你可以实现与MagicMock.assert_called_with()完全相反的操作

        call_args_listcall 对象的列表。每个call 对象代表一个对模拟可调用对象的调用。

        >>> 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)]
        

        使用call 对象很容易,因为您可以将其与长度为 2 的元组进行比较,其中第一个组件是包含相关调用的所有位置参数的元组,而第二个组件是关键字的字典论据。

        >>> ((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
        

        所以,解决 OP 的具体问题的一种方法是

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

        请注意,通过这种方式,您现在可以检查是否已使用一组特定的参数调用它,而不仅仅是检查是否已通过 MagicMock.called 调用了一个模拟的可调用对象。

        这很有用。假设您要测试一个函数,该函数接受一个列表并调用另一个函数 compute(),仅当列表的每个值满足特定条件时。

        您现在可以模拟compute,并测试它是否已在某些值上被调用,但在其他值上没有。

        【讨论】:

          【解决方案7】:

          根据 python 标准库文档,python >= 3.5 one should use assert_not_called

          my_var.assert_not_called()
          

          注意:Python 3.5。 9 于 2019 年 11 月 1 日发布,此问题于 2013 年首次提出。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-12-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多