Mock vs MagicMock

我的理解是MagicMock模拟的超集,它自动执行“魔法方法”,从而无缝地提供对列表、迭代等的支持……那么普通模拟存在的原因是什么?这难道不是一个可以忽略的MagicMock的精简版本吗?模拟类是否知道任何在MagicMock中不可用的技巧?

83848 次浏览

使用Mock你可以模拟魔术方法,但你必须定义它们。MagicMock有“大多数神奇方法的默认实现。”

如果您不需要测试任何神奇的方法,Mock就足够了,并且不会给您的测试带来很多无关的东西。如果您需要测试很多魔术方法,MagicMock将为您节省一些时间。

这是python的官方文档 说:< / p >

在大多数示例中,Mock和MagicMock类是可互换的。由于MagicMock是功能更强的类,因此在默认情况下使用它是明智的。

普通模拟存在的原因是什么?

Mock的作者Michael ford在一个非常相似的问题在Pycon 2011 (31:00)中写道:

问:为什么MagicMock是一个单独的东西,而不是仅仅折叠到默认的模拟对象的能力?

一个合理的答案是MagicMock的工作方式是通过创建新的mock和来预配置所有这些协议方法 设置它们,如果每个新mock创建一堆新mock 将这些设置为协议方法,然后是所有这些协议方法 创建了更多的模拟,并将它们设置在它们的协议方法上, 你得到了无限递归。

如果你想访问你的模拟作为容器对象是一个 错误,你不想让它起作用吗?如果每个mock都自动 得到了所有的协议方法,那么它就变得更加困难 那而且,MagicMock为你做了一些预配置, 设置可能不合适的返回值,所以我认为 拥有这种便利的,什么都有的会更好吗 预配置和可用为您,但您也可以采取普通 模拟对象,只需要配置你想要存在的魔法方法 简单的答案是:只要在任何地方使用MagicMock

首先,MagicMockMock的子类。

class MagicMock(MagicMixin, Mock)

因此,MagicMock提供了Mock所提供的一切,甚至更多。不要把Mock看作是MagicMock的简化版本,而应该把MagicMock看作是Mock的扩展版本。这将解决您的问题,即为什么存在Mock以及Mock在MagicMock之上提供了什么。

其次,MagicMock提供了许多/大多数magic方法的默认实现,而Mock没有。有关魔术方法的更多信息,请参见在这里

一些魔术方法的例子:

>>> int(Mock())
TypeError: int() argument must be a string or a number, not 'Mock'
>>> int(MagicMock())
1
>>> len(Mock())
TypeError: object of type 'Mock' has no len()
>>> len(MagicMock())
0

这些可能不是很直观(至少对我来说不是很直观):

>>> with MagicMock():
...     print 'hello world'
...
hello world
>>> MagicMock()[1]
<MagicMock name='mock.__getitem__()' id='4385349968'>

当这些方法第一次被调用时,你可以“看到”添加到MagicMock的方法:

>>> magic1 = MagicMock()
>>> dir(magic1)
['assert_any_call', 'assert_called_once_with', ...]
>>> int(magic1)
1
>>> dir(magic1)
['__int__', 'assert_any_call', 'assert_called_once_with', ...]
>>> len(magic1)
0
>>> dir(magic1)
['__int__', '__len__', 'assert_any_call', 'assert_called_once_with', ...]

那么,为什么不一直使用MagicMock呢?

回到您的问题是:您是否同意默认的magic方法实现?例如,mocked_object[1]是否可以不出错?您是否可以接受由于魔术方法实现已经存在而产生的任何意外后果?

如果这些问题的答案是肯定的,那么继续使用MagicMock。否则,请继续使用Mock。

我发现了另一个特殊情况,其中简单的 Mock可能比MagicMock更有用:

In [1]: from unittest.mock import Mock, MagicMock, ANY
In [2]: mock = Mock()
In [3]: magic = MagicMock()
In [4]: mock.foo == ANY
Out[4]: True
In [5]: magic.foo == ANY
Out[5]: False

比较ANY可能是有用的,例如,比较两个字典之间的几乎每个键,其中一些值是使用mock计算的。

如果你使用Mock,这将是有效的:


self.assertDictEqual(my_dict, {
'hello': 'world',
'another': ANY
})

而如果你使用了MagicMock,则会引发AssertionError