断言未使用 Mock 调用函数/方法

Posted

技术标签:

【中文标题】断言未使用 Mock 调用函数/方法【英文标题】:Assert a function/method was not called using 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

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

【问题讨论】:

正如@Ahmet 在他的回答中指出的那样,现在支持 assert_not_call ,也在后端 (docs.python.org/3/library/…) 中。 【参考方案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 年首次提出。

【讨论】:

以上是关于断言未使用 Mock 调用函数/方法的主要内容,如果未能解决你的问题,请参考以下文章

模拟 - 测试是否在不指定参数的情况下调用方法

使用mock模拟解决测试中依赖问题

如何使用OCMock验证在Objective C中不调用异步方法?

jest中的mock,jest.fn()jest.spyOn()jest.mock()

jest中的mock,jest.fn()jest.spyOn()jest.mock()

jest中的mock,jest.fn()jest.spyOn()jest.mock()