断言未使用 Mock 调用函数/方法
Posted
技术标签:
【中文标题】断言未使用 Mock 调用函数/方法【英文标题】:Assert a function/method was not called using Mock 【发布时间】:2012-08-24 14:32:10 【问题描述】:我正在使用 Mock 库来测试我的应用程序,但我想断言某些函数没有被调用。 Mock 文档讨论了 mock.assert_called_with
和 mock.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 mock
和 from 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 >= 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_list
是call
对象的列表。每个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 调用函数/方法的主要内容,如果未能解决你的问题,请参考以下文章
如何使用OCMock验证在Objective C中不调用异步方法?
jest中的mock,jest.fn()jest.spyOn()jest.mock()