蟒蛇、单元测试和模拟进口
-
05-07-2019 - |
题
我在一个项目,我们开始重构一些大规模的码基础。一个问题,即立即兴起是每个文件的进口许多其他文件。我怎么优雅的方式模拟的,这在我的单元的测试没有改变的实际代码这样我就可以开始写的单元的测试吗?
作为一个例子:该文件的功能我需要测试,进口十其他文件,这是我们的软件并不python核心库。
我希望能够运行单位测试作为单独作为可能,并为现在我只要的测试职能,并不取决于事情的文件,都是进口的。
谢谢你所有的答案。
我真的不知道我想做什么从一开始,但现在我觉得我知道的。
问题是,一些进口是唯一可能的时候整个应用程序已运行,因为一些第三方汽车的魔法。所以我不得不做一些桩为这些模块在一个目录,其中我指出了与sys.路径
现在我可以进口该文件,该文件包含的职能我想写测试对于我的单元测试文件没有抱怨缺少的模块。
解决方案
如果要在导入模块的同时确保它不导入任何内容,可以替换 __ 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
的第二个参数是 想要在内部模块中导入的模块名称列表。
此示例可以进一步修改为例如导入除了期望之外的其他模块,而不是仅仅导入它,甚至用自己的一些自定义对象模拟模块对象。
其他提示
如果您真的想要使用python导入机制,请查看 <代码> ihooks 模块。它提供了用于更改内置的 __ import __
行为的工具。但是你的问题并不清楚为什么你需要这样做。
"进口许多其他文件"?进口许多其他文件一部分的你定制的代码?或进口许多其他文件一部分的Python分布?或进口许多其他开源项目的文件?
如果你进口没有工作,你有一个"简单" PYTHONPAT
H问题。得到所有各种项目目录到 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
是搜索了,我们可以控制获取用于通过调整的道路上。
一旦你有"一切",它将成为一个问题的信任。
要么
- 你相信的东西(即,蟒蛇的基础代码)和只进口。
或
你不相信的东西(即自己的代码)和你
- 测试它分开
- 模拟它的独立测试。
你会试Python库?如果是这样,你已经有了很多的工作。如果没有,那么,你应该也许只是模拟出来的东西你实际上是要测试。
如果您想在单元测试之前进行快速而肮脏的修复,则无需进行任何困难的操作。
如果单元测试与您要测试的代码位于同一文件中,只需从 globals()
字典中删除不需要的模块。
这是一个相当冗长的例子:假设你有一个带有内容的模块 impp.py
:
value = 5
现在,在您的测试文件中,您可以写:
>>> import impp
>>> print globals().keys()
>>> def printVal():
>>> print impp.value
['printVal', '__builtins__', '__file__', 'impp', '__name__', '__doc__']
请注意, impp
属于全局变量,因为它是导入的。调用使用 impp
模块的函数 printVal
仍然有效:
>>> printVal()
5
但是现在,如果从 globals()
中删除 impp
键...
>>> 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
。不确定这对于包导入是如何工作的,但绝对导入应该没问题。