Mock vs MagicMock

Vla*_*tov 125 python mocking

我的理解是,MagicMockMock的超集,它自动执行"魔术方法",从而无缝地提供对列表,迭代等的支持......那么普通Mock存在的原因是什么?这不仅仅是MagicMock的精简版,几乎可以被忽略吗?Mock类是否知道MagicMock中没有的任何技巧?

Ryn*_*ett 91

普通莫克存在的原因是什么?

Mock的作者Michael Foord 在Pycon 2011( 31:00)发表了一个非常类似的问题:

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

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

如果您希望将模拟作为容器对象访问是一个错误 - 您不希望它工作怎么办?如果每个模拟都自动获得每个协议方法,那么这样做会变得更加困难.而且,MagicMock会为你做一些预先配置,设置可能不合适的返回值,所以我认为最好有一个具有预配置和可用的一切的便利,但你也可以采取普通的模拟对象,只需配置你想要存在的魔术方法......

简单的答案是:如果这是您想要的行为,只需在任何地方使用MagicMock.

  • @laike9m 我读的建议是相反的:只使用 MagicMock,除非你知道你在做什么并且想要特定的行为,在这种情况下从 Mock 开始并推出你自己的。 (11认同)
  • 我认为更好的答案是:如果你知道你在做什么就使用MagicMock,否则使用Mock. (8认同)
  • @Robino我不这么认为。肖恩·雷德蒙德的回答下的评论很好地解释了这一点。 (3认同)
  • 官方文档另有建议“由于 MagicMock 是功能更强大的类,因此默认情况下使用它是一个明智的选择。” https://docs.python.org/dev/library/unittest.mock-examples.html#mock-patching-methods (3认同)

Sea*_*ond 49

使用Mock,您可以模拟魔术方法,但您必须定义它们.MagicMock拥有"大多数魔术方法的默认实现"..

如果您不需要测试任何魔术方法,Mock就足够了,并且不会给您的测试带来很多无关紧要的事情.如果你需要测试很多魔术方法,MagicMock会为你节省一些时间.

  • 测试应该是最小的,并且模拟对象应该具有最低限度的功能,以便您确切地确定您正在测试的内容.如果您使用MagicMock只是因为它做得更多但是您没有明确地测试所有"更多",则由于默认的MagicMock行为而导致测试失败的风险.这个失败可能反映了MagicMock的默认值,而不是它应该模拟的东西.更糟糕的是,当它应该失败时,你冒着测试_succeeding_的风险.风险很小但如果发生这种情况会浪费你很多时间. (35认同)
  • 果然我已经阅读了文档。这并没有回答我的问题——如果 MagicMock 的功能完全相同,而且还有更多功能,为什么还要费心使用普通的 Mock 呢?我在测试中没有看到任何无关的东西 - 只需使用不同的名称即可。那么问题在哪里呢? (3认同)
  • 我认为它就像使用普通 JS 和 Jquery。当然,您*可以*使用 Jquery 来完成所有 JS,但在某些情况下,您只想使用完成工作所需的最少工具。我发现这些案例通常要么极其简单,要么极其复杂。 (3认同)

use*_*654 42

首先,MagicMock是一个子类Mock.

class MagicMock(MagicMixin, Mock)
Run Code Online (Sandbox Code Playgroud)

因此,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
Run Code Online (Sandbox Code Playgroud)

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

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

您可以"看到"添加到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', ...]
Run Code Online (Sandbox Code Playgroud)

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

回答你的问题是:你对默认的魔术方法实现没问题吗?例如,mocked_object[1]没有错误可以吗?由于神奇的方法实现已经存在,你是否可以承担任何意想不到的后果?

如果这些问题的答案是肯定的,那么请继续使用MagicMock.否则,坚持模拟.


use*_*464 11

这是python的官方文档 所说的:

在大多数这些示例中,Mock和MagicMock类是可互换的.由于MagicMock是功能更强大的类,因此默认使用它是一个明智的选择.

  • 这对我来说听起来不对。我喜欢使用最简单的工具来满足我的需求。不是那些花里胡哨最多的,我不知道它们是否有用。 (4认同)

Man*_*anu 9

我发现了另一个特殊情况,简单 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
Run Code Online (Sandbox Code Playgroud)

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

如果您使用的是Mock


self.assertDictEqual(my_dict, {
  'hello': 'world',
  'another': ANY
})
Run Code Online (Sandbox Code Playgroud)

AssertionError如果你使用过,它会引发MagicMock