我在测试环境中的3个不同目录中分别有一个test_a.py,a.py和b.py。
b.py
class SBD():
def __init__(self):
print("SBD created (In B)")
a.py
import b
from b import *
print("In module A")
def fun1():
a=SBD()
print("SBD created(In A)")
test_a.py
import unittest
import sys
from unittest.mock import Mock,patch,MagicMock
sys.path.append("../abc/")
import b as c
sys.modules['b'] = MagicMock(spec=c)
sys.path.append("../xyz/")
import a
class TestStringMethods(unittest.TestCase):
def test_isupper(self):
a.fun1()
if __name__ == '__main__':
unittest.main()
在实际情况下,b.py将具有多个类,而我想模拟所有这些类,因此我尝试使用相同的规格模拟模块b。但是,当我运行test_a.py时,出现错误,提示未定义“ SBD”。我在这里做错了什么?
答案 0 :(得分:0)
MagicMock
实例没有为模块提供与导入机器相同的信息。即使使用spec
,在模拟中也没有定义实际的SDB
属性,因此from b import *
找不到它。
from *
导入机制尝试两种不同的方法:
__all__
;如果定义,则必须是要导入的名称字符串列表。__all__
,则将使用__dict__
属性的键,过滤出以下划线开头的名称。由于您的b
模块未定义__all__
列表,因此将使用__dict__
键。对于针对某个模块的MagicMock
实例,__dict__
属性仅包含带下划线_
和mock_calls
属性的名称。 from b import *
仅导入mock_calls
:
>>> import a as c
>>> module_mock = MagicMock(spec=c)
>>> [n for n in module_mock.__dict__ if n[:1] != '_']
['method_calls']
我强烈建议反对模拟整个模块;这样做会要求您推迟导入a
,并且脆弱。该补丁是永久性的(测试结束时不会自动撤消),并且不支持重复运行测试或以随机顺序运行测试。
但是如果您必须进行此项工作,则可以先向模拟对象添加__all__
属性:
sys.modules['b'] = MagicMock(spec=c, __all__=[n for n in c.__dict__ if n[:1] != '_'])
我个人而言,a)避免完全使用from module import *
语法。如果无论如何我都不能阻止它使用,下一步将是在导入后将补丁应用于a
,遍历b
模块以获得指定的替换: / p>
# avoid manipulating sys.path if at all possible. Move that to a PYTHONPATH
# variable or install the modules properly.
import unittest
from unittest import mock
import a
import b
class TestStringMethods(unittest.TestCase):
def setUp(self):
# mock everything `from b import *` would import
b_names = getattr(b, '__all__', None)
if b_names is None:
b_names = [n for n in b.__dict__ if n[:1] != '_']
self.b_mocks = {}
for name in b_names:
orig = getattr(b, name, None)
if orig is None:
continue
self.b_mocks[name] = mock.patch.object(a, name, spec=orig)
self.b_mocks[name].start()
self.addCleanup(self.b_mocks[name].stop)
def test_isupper(self):
a.fun1()
这将使sys.modules['b']
保持不变,并处理与from *
完全相同的名称。测试结束后,再次删除补丁。
以上测试输出:
$ python test_a.py
In module A
SBD created(In A)
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK