我正在参与一个项目,我们正在开始重构一些庞大的代码库。立即出现的一个问题是每个文件都会导入许多其他文件。我如何以一种优雅的方式在单元测试中模拟它,而不必更改实际代码,以便我可以开始编写单元测试?
举个例子:带有我想要测试的功能的文件,导入了其他十个文件,这些文件是我们软件的一部分,而不是Python核心库。
我希望能够尽可能单独地运行单元测试,现在我只想测试不依赖于正在导入的文件中的内容的函数。
谢谢大家的回答。
我从一开始就不知道自己想做什么,但现在我想我知道了。
问题是,由于某些第三方自动魔法,某些导入仅在整个应用程序运行时才可能进行。所以我必须在我用 sys.path 指出的目录中为这些模块制作一些存根
现在我可以导入包含我想要在单元测试文件中编写测试的函数的文件,而不会抱怨缺少模块。
__import__
内置函数。例如使用这个类:
class ImportWrapper(object):
def __init__(self, real_import):
self.real_import = real_import
def wrapper(self, wantedModules):
def inner(moduleName, *args, **kwargs):
if moduleName in wantedModules:
print "IMPORTING MODULE", moduleName
self.real_import(*args, **kwargs)
else:
print "NOT IMPORTING MODULE", moduleName
return inner
def mock_import(self, moduleName, wantedModules):
__builtins__.__import__ = self.wrapper(wantedModules)
try:
__import__(moduleName, globals(), locals(), [], -1)
finally:
__builtins__.__import__ = self.real_import
在你的测试代码中,不要写
import myModule
,而是写:
wrapper = ImportWrapper(__import__)
wrapper.mock_import('myModule', [])
mock_import
的第二个参数是您do想要在内部模块中导入的模块名称列表。 这个例子可以进一步修改为例如导入不需要的其他模块,而不是不导入它,甚至用您自己的一些自定义对象来模拟模块对象。
如果您的导入不起作用,那么您将遇到一个“简单”
PYTHONPATH
问题。将所有各种项目目录放入可用于测试的
PYTHONPATH
。我们有一个相当复杂的路径,在Windows中我们这样管理它
@set Part1=c:\blah\blah\blah
@set Part2=c:\some\other\path
@set that=g:\shared\stuff
set PYTHONPATH=%part1%;%part2%;%that%
我们将路径的每一部分分开,以便我们 (a) 知道事物从哪里来,并且 (b) 在我们移动事物时可以管理变化。由于
PYTHONPATH
是按顺序搜索的,我们可以通过调整路径上的顺序来控制使用什么。一旦你拥有“一切”,这就变成了信任问题。
要么
如果单元测试与您要测试的代码位于同一文件中,只需从
globals()
字典中删除不需要的模块即可。这是一个相当冗长的示例:假设您有一个模块
impp.py
,其内容为:
value = 5
现在,在您的测试文件中您可以编写:
>>> import impp
>>> print globals().keys()
>>> def printVal():
>>> print impp.value
['printVal', '__builtins__', '__file__', 'impp', '__name__', '__doc__']
请注意,
impp
属于全局变量,因为它是导入的。调用使用
printVal
模块的函数
impp
仍然有效:
>>> printVal()
5
但是现在,如果您从
impp
中删除
globals()
键...
>>> del globals()['impp']
>>> print globals().keys()
['printVal', '__builtins__', '__file__', '__name__', '__doc__']
...并尝试拨打
printVal()
,您会得到:
>>> printVal()
Traceback (most recent call last):
File "test_imp.py", line 13, in <module>
printVal()
File "test_imp.py", line 5, in printVal
print impp.value
NameError: global name 'impp' is not defined
...这可能正是您想要实现的目标。
要在单元测试中使用它,您可以在运行测试套件之前删除全局变量,例如在
__main__
:
if __name__ == '__main__':
del globals()['impp']
unittest.main()
上面中,您说您想说服 python 某些模块已经被导入。这看起来仍然是一个奇怪的目标,但如果这确实是你想要做的,原则上你可以偷偷地躲在导入机制的背后,并改变sys.modules
。不确定这对于包导入如何工作,但对于绝对导入应该没问题。
相关问题
从 app.services 导入搜索search() 位于 search_service.py 内部 其中有 10 个来自我的本地代码库的导入,其中之一是秘密管理器,它在启动时自动初始化。因此,当我导入搜索时,它会尝试连接到机密管理器。
有没有办法只导入搜索方法来测试它,而不需要 python 尝试导入 search_service.py 中的所有导入。
TIA。