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

我正在使用 Mock 库来测试我的应用程序,但是我想声明某个函数没有被调用。Mock 文档讨论了类似于 mock.assert_called_withmock.assert_called_once_with的方法,但是我没有找到类似于 mock.assert_not_called的方法或者与校验 Mock 是否为 没打电话相关的方法。

我可以用下面这样的东西,虽然它看起来既不酷也不蟒蛇:

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

有什么办法吗?

154194 次浏览

这对你的案子应该有用;

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

您可以检查 called属性,但是如果断言失败,接下来您想知道的是 关于意外调用,因此您可以从一开始就安排显示该信息。使用 unittest,你可以检查 call_args_list的内容:

self.assertItemsEqual(my_var.call_args_list, [])

如果失败了,它会发出这样的信息:

AssertionError: Element counts were not equal:
First has 0, Second has 1:  call('first argument', 4)

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

  • 断言真实
  • AssertFalse
  • 断言平等

和类似的(在 Python 文档中你可以找到其余的)。

在您的示例中,如果 调用的属性是 错误,我们可以简单地断言,这意味着没有调用该方法。

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

从其他答案来看,除了 @ 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,并测试它是否在某些值上被调用,而在其他值上没有被调用。

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

升级你的就行了

pip install mock --upgrade

使用 python >= 3.5可以使用 mock_object.assert_not_called()

我首选的检查 mock 是否被调用的习惯用法如下: self.assertEqual(post_mock.call_count, 0)

好处:

  1. 确保调用 mock 的次数正确。
  2. 使用内部 PyTest 断言平等函数。