问题:模拟与魔术模拟
我的理解是MagicMock是Mock的超集,它自动执行“魔术方法”,从而无缝地提供对列表,迭代等的支持。那么,为什么存在普通Mock的原因是什么?难道这不是MagicMock的简化版本,实际上可以忽略吗?Mock类是否知道MagicMock中没有的任何技巧?
回答 0
纯模拟存在的原因是什么?
Mock的作者Michael Foord 在Pycon 2011(31:00)上回答了一个非常类似的问题:
问:为什么MagicMock做了一件单独的事情,而不仅仅是将功能折叠到默认的模拟对象中?
答:一个合理的答案是MagicMock的工作方式是通过创建新的Mocks并对其进行设置来预先配置所有这些协议方法,因此,如果每个新的模拟都创建了一堆新的模拟并将它们设置为协议方法,则所有这些协议方法创建了一堆更多的模拟并将它们设置在其协议方法上,您可以无限递归…
如果您想将模拟作为容器对象访问是错误的,那又不想怎么办?如果每个模拟都自动获得了每种协议方法,那么这样做就变得更加困难。而且,MagicMock为您执行了一些预配置,设置了可能不合适的返回值,所以我认为最好有这种便利,它具有所有预配置的功能并可供您使用,但是您也可以进行普通的模拟对象,然后配置您想要存在的魔术方法…
简单的答案是:如果您要这样做,那就在任何地方使用MagicMock。
回答 1
使用Mock,您可以模拟魔术方法,但必须定义它们。MagicMock具有“大多数魔术方法的默认实现”。。
如果您不需要测试任何魔术方法,那么Mock就足够了,并且不会在测试中带来很多无关紧要的东西。如果您需要测试许多魔术方法,MagicMock将为您节省一些时间。
回答 2
首先MagicMock
是的子类Mock
。
class MagicMock(MagicMixin, Mock)
结果,MagicMock提供了Mock提供的一切以及更多功能。与其认为Mock是MagicMock的精简版,不如认为MagicMock是Mock的扩展版。这应该解决您有关Mock为什么存在以及Mock在MagicMock之上提供了什么的问题。
其次,MagicMock提供了许多魔术方法的默认实现,而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呢?
回到您的问题是:您可以使用默认的魔术方法实现吗?例如,mocked_object[1]
可以不出错吗?您是否可以接受由于魔术方法实现而导致的任何意外后果?
如果对这些问题的回答为“是”,请继续使用MagicMock。否则,坚持模拟。
回答 3
这就是python的官方文档 所说的:
在大多数这些示例中,Mock和MagicMock类是可互换的。由于MagicMock是功能更强大的类,因此默认情况下会使用一个明智的类。
回答 4
我发现了另一种特殊情况,其中简单 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
:
self.assertDictEqual(my_dict, {
'hello': 'world',
'another': ANY
})
AssertionError
如果您使用过,它将引发一个MagicMock