我的理解是MagicMock是Mock的超集,自动执行“魔术方法”,从而无缝支持列表、迭代等操作...那么为什么还需要原始的Mock存在呢?它不就是一个可以被忽略的简化版本的MagicMock吗?Mock类是否知道任何在MagicMock中不可用的技巧呢?
Mock的作者Michael Foord在2011年Pycon(31:00)上回答了一个非常类似的问题:
Q:为什么要将MagicMock作为独立的事物,而不是将其能力合并到默认的mock对象中?
A:其中一个合理的答案是,MagicMock的工作方式是通过创建新的Mock并设置它们来预配置所有这些协议方法,因此如果每个新的mock创建了一堆新的mock并将这些mock设置为协议方法,然后所有这些协议方法都创建了一堆更多的mock并将它们设置在它们的协议方法中,那么就会产生无限递归……
如果你希望访问你的mock作为容器对象时会出现错误——你不希望它起作用怎么办?如果每个mock都自动获取了每个协议方法,那么就变得更加困难。另外,MagicMock还为你做了一些预配置,设置可能不合适的返回值,所以我认为最好有一个方便的方法,将所有的预配置和可用性都预先配置好,但你也可以使用普通的mock对象,只需配置你想要存在的魔术方法即可……
简单的答案是:如果这是你想要的行为,那就在任何地方都使用MagicMock。
使用Mock,您可以模拟魔术方法,但必须定义它们。MagicMock具有“大多数魔术方法的默认实现。”。
如果您不需要测试任何魔术方法,则Mock就足够了,并且不会在测试中带来许多无关的东西。如果您需要测试许多魔术方法,则使用MagicMock可以节省一些时间。
首先,MagicMock
是 Mock
的子类。
class MagicMock(MagicMixin, 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'>
>>> 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。否则,坚持使用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
,则此方法将有效:
self.assertDictEqual(my_dict, {
'hello': 'world',
'another': ANY
})
如果你使用了MagicMock
,它会引发一个AssertionError
异常。
unittest.mock.create_autospec()
会给你一个MagicMock
。你应该使用它。 - z33k