40

我正在尝试让 mock.patch 处理以下示例代码:

from mock import patch
from collections import defaultdict

with patch('collections.defaultdict'):
  d = defaultdict()
  print 'd:', d

这将输出以下内容:

d: defaultdict(None, {})

这意味着 defaultdict 没有被修补。

如果我用直接导入语句替换 from/import 语句,它可以工作:

from mock import patch
import collections

with patch('collections.defaultdict'):
 d = collections.defaultdict()
 print 'd:', d

输出是:

d: <MagicMock name='defaultdict()' id='139953944084176'>

有没有办法使用 from/import 修补呼叫?

谢谢

4

3 回答 3

60

如果您要修补同一模块中的某些内容,则可以使用__main__

from mock import patch
from collections import defaultdict

with patch('__main__.defaultdict'):
    d = defaultdict()
    print 'd:', d

但是,如果您要为导入的模块模拟​​某些内容,则需要使用该模块的名称,以便修补正确的引用(或名称):

# foo.py

from collections import defaultdict

def bar():
    return defaultdict()


# foo_test.py    

from mock import patch
from foo import bar

with patch('foo.defaultdict'):
    print bar()

这里的重点是补丁想要它正在修补的东西的完整路径。在修补当前模块中的某些内容时,这看起来有点奇怪,因为人们不经常使用__main__(或者必须参考当前模块,就此而言)。

于 2012-07-05T19:47:36.460 回答
12

patch通过修补名称来工作。collections.defaultdict如果您使用名称defaultdict(在本地命名空间中)访问对象,则无法通过修补名称来实现任何目标。请参阅https://docs.python.org/3/library/unittest.mock.html#where-to-patch上的文档

于 2012-07-05T19:47:48.747 回答
3

在这种情况下,名称可能会非常混乱。我们总是想在命名空间中模拟一个类定义。命名空间是进行导入的模块。类定义是该命名空间中使用的名称。

让我们举一个具体的例子:

  • myproj.utilities 模块包含 Actor 类
  • myproj.application 将其导入为from myproj.utilities import Actor
  • 我的测试应该执行 my.proj.application 并模拟我们的 Actor

myproj.utilities.py

class Actor:
    def __init__(name):
        self.name = name

myproj.application.py

from myproj.utilities import Actor

class App:
    def __init__(name):
        self.actor = Actor(name)

测试代码

from mock import patch
from myproj.application import App

test:
  # format: patch('<namespace>.<Class>')
  # the namespace in which we with to mock
  # the class definition we wish to mock
  with patch('myproj.application.Actor'):
      app = App('Someone')
      print( type(app.actor) ) # expect a MagicMock

我尝试了其他几种方法,这一种对我来说效果很好。我没有测试上面的代码,而是从我自己的具体案例中对其进行了概括。所以,可能有点偏了。

于 2019-02-12T17:24:45.610 回答