问题:如何模拟导入
模块A
包括import B
在其顶部。然而在试验条件下,我想嘲笑 B
的A
(模拟A.B
)和进口完全避免B
。
实际上,B
并不是故意在测试环境中安装的。
A
是被测单元。我必须导入A
所有功能。B
是我需要模拟的模块。但是,如果第一件事就是导入,我该如何B
在其中模拟A
并停止A
导入实数?B
A
B
(未安装B的原因是我使用pypy进行了快速测试,但不幸的是B尚未与pypy兼容。)
怎么办呢?
回答 0
您可以sys.modules['B']
在导入之前分配给以A
获得所需的内容:
test.py:
import sys
sys.modules['B'] = __import__('mock_B')
import A
print(A.B.__name__)
A.py:
import B
注意B.py不存在,但是运行时test.py
不会返回错误并显示print(A.B.__name__)
print mock_B
。您仍然必须mock_B.py
在模拟B
实际功能/变量/等的地方创建一个。或者,您可以直接分配一个Mock()
:
test.py:
import sys
sys.modules['B'] = Mock()
import A
回答 1
__import__
可以使用’mock’库来模拟内置函数,以实现更多控制:
# Store original __import__
orig_import = __import__
# This will be the B module
b_mock = mock.Mock()
def import_mock(name, *args):
if name == 'B':
return b_mock
return orig_import(name, *args)
with mock.patch('__builtin__.__import__', side_effect=import_mock):
import A
说A
看起来像:
import B
def a():
return B.func()
A.a()
返回b_mock.func()
也可以被嘲笑。
b_mock.func.return_value = 'spam'
A.a() # returns 'spam'
Python 3的注意事项:
如3.0的变更日志所述,__builtin__
现名为builtins
:
将模块重命名
__builtin__
为builtins
(删除下划线,添加“ s”)。
如果更换这个答案的代码工作正常__builtin__
通过builtins
为Python 3。
回答 2
如何模拟导入(模拟AB)?
模块A在其顶部包括导入B。
容易,只需在导入它之前在sys.modules中模拟该库:
if wrong_platform():
sys.modules['B'] = mock.MagicMock()
然后,只要A
不依赖于从B对象返回的特定类型的数据:
import A
应该工作。
您还可以嘲笑import A.B
:
即使您有子模块,此方法也有效,但是您将需要模拟每个模块。说你有这个:
from foo import This, That, andTheOtherThing
from foo.bar import Yada, YadaYada
from foo.baz import Blah, getBlah, boink
要进行模拟,只需在导入包含以上内容的模块之前执行以下操作:
sys.modules['foo'] = MagicMock()
sys.modules['foo.bar'] = MagicMock()
sys.modules['foo.baz'] = MagicMock()
(我的经验:我有一个可以在一个平台Windows上运行的依赖项,但是在运行日常测试的Linux上却不起作用。所以我需要为我们的测试模拟该依赖项。幸运的是,这是一个黑盒子,所以我不需要进行很多互动。)
模拟副作用
附录:实际上,我需要模拟花费一些时间的副作用。所以我需要一个对象的方法来睡一秒钟。那会像这样工作:
sys.modules['foo'] = MagicMock()
sys.modules['foo.bar'] = MagicMock()
sys.modules['foo.baz'] = MagicMock()
# setup the side-effect:
from time import sleep
def sleep_one(*args):
sleep(1)
# this gives us the mock objects that will be used
from foo.bar import MyObject
my_instance = MyObject()
# mock the method!
my_instance.method_that_takes_time = mock.MagicMock(side_effect=sleep_one)
然后,就像真实方法一样,代码需要一些时间才能运行。
回答 3
我意识到我在这里参加聚会有点晚了,但这是一种通过mock
库自动执行此操作的疯狂方法:
(这是一个示例用法)
import contextlib
import collections
import mock
import sys
def fake_module(**args):
return (collections.namedtuple('module', args.keys())(**args))
def get_patch_dict(dotted_module_path, module):
patch_dict = {}
module_splits = dotted_module_path.split('.')
# Add our module to the patch dict
patch_dict[dotted_module_path] = module
# We add the rest of the fake modules in backwards
while module_splits:
# This adds the next level up into the patch dict which is a fake
# module that points at the next level down
patch_dict['.'.join(module_splits[:-1])] = fake_module(
**{module_splits[-1]: patch_dict['.'.join(module_splits)]}
)
module_splits = module_splits[:-1]
return patch_dict
with mock.patch.dict(
sys.modules,
get_patch_dict('herp.derp', fake_module(foo='bar'))
):
import herp.derp
# prints bar
print herp.derp.foo
这是如此荒谬的原因是,当发生导入时,python基本上会这样做(例如from herp.derp import foo
)
- 是否
sys.modules['herp']
存在?否则导入。如果还没有ImportError
- 是否
sys.modules['herp.derp']
存在?否则导入。如果还没有ImportError
- 获取属性
foo
的sys.modules['herp.derp']
。其他ImportError
foo = sys.modules['herp.derp'].foo
这种被黑客入侵的解决方案有一些缺点:如果模块路径中的其他内容依赖于其他内容,则将其拧紧。而且,这仅适用于内联导入的内容,例如
def foo():
import herp.derp
要么
def foo():
__import__('herp.derp')
回答 4
亚伦·霍尔的答案对我有用。只想提一件事,
如果A.py
你愿意
from B.C.D import E
然后test.py
您必须模拟路径中的每个模块,否则会得到ImportError
sys.modules['B'] = mock.MagicMock()
sys.modules['B.C'] = mock.MagicMock()
sys.modules['B.C.D'] = mock.MagicMock()
回答 5
我找到了在Python中模拟导入的好方法。这是Eric的Zaadi解决方案,我在Django应用程序中使用了该解决方案。
我有一个类SeatInterface
,它是Seat
模型类的接口。所以在我的seat_interface
模块中,我有这样一个导入:
from ..models import Seat
class SeatInterface(object):
(...)
我想为SeatInterface
模拟Seat
类创建隔离测试FakeSeat
。问题是-在Django应用程序关闭的情况下,tu运行离线测试的方式。我有以下错误:
配置不正确:请求的设置为BASE_DIR,但未配置设置。您必须先定义环境变量DJANGO_SETTINGS_MODULE或调用settings.configure()才能访问设置。
在0.078秒内进行了1次测试
失败(错误= 1)
解决方案是:
import unittest
from mock import MagicMock, patch
class FakeSeat(object):
pass
class TestSeatInterface(unittest.TestCase):
def setUp(self):
models_mock = MagicMock()
models_mock.Seat.return_value = FakeSeat
modules = {'app.app.models': models_mock}
patch.dict('sys.modules', modules).start()
def test1(self):
from app.app.models_interface.seat_interface import SeatInterface
然后测试神奇地运行OK :)
。
在0.002秒内进行1次测试好
回答 6
如果这样做,import ModuleB
您实际上是在__import__
按以下方式调用内置方法:
ModuleB = __import__('ModuleB', globals(), locals(), [], -1)
您可以通过导入__builtin__
模块并对该方法进行包装来覆盖此__builtin__.__import__
方法。或者,您可以使用模块中的NullImporter
挂钩imp
。捕获异常并在except
-block中模拟您的模块/类。
指向相关文档的指针:
我希望这有帮助。是HIGHLY劝你踏进Python编程的更神秘的周边和一)扎实的了解,你真正要实现和B)的影响的深入理解什么是重要的。