我想在python中伪造一个包。我想定义一些代码可以做的事情
from somefakepackage.morefakestuff import somethingfake
并且somefakepackage在代码中定义,因此下面的所有内容都是如此。那可能吗?这样做的原因是欺骗我的单元测试,我在python路径中得到了一个包(或者我在标题中说的一个模块),这实际上只是为了这个单元测试而模拟的东西。
谢谢!
答案 0 :(得分:28)
不确定。定义一个类,将所需的东西放在其中,将类分配给sys.modules["classname"]
。
class fakemodule(object):
@staticmethod
def method(a, b):
return a+b
import sys
sys.modules["package.module"] = fakemodule
您也可以使用单独的模块(称之为fakemodule.py
):
import fakemodule, sys
sys.modules["package.module"] = fakemodule
答案 1 :(得分:13)
是的,你可以伪造一个模块:
from types import ModuleType
m = ModuleType("fake_module")
import sys
sys.modules[m.__name__] = m
# some scripts may expect a file
# even though this file doesn't exist,
# it may be used by Python for in error messages or introspection.
m.__file__ = m.__name__ + ".py"
# Add a function
def my_function():
return 10
m.my_function = my_function
注意,在这个例子中,它使用了一个实际的模块(ModuleType
)
Python代码可能需要模块(而不是虚拟类)。
这可以变成效用函数:
def new_module(name, doc=None):
import sys
from types import ModuleType
m = ModuleType(name, doc)
m.__file__ = name + '.py'
sys.modules[name] = m
return m
print(new_module("fake_module", doc="doc string"))
现在可以运行其他脚本:
import fake_module
答案 2 :(得分:4)
您可以使用类似somethingfake
的行来伪造它:
try:
from somefakepackage.morefakestuff import somethingfake
except ImportError:
class somethingfake(object):
# define what you'd expect of somethingfake, e.g.:
@staticmethod
def somefunc():
...
somefield = ...
答案 3 :(得分:4)
我从其他答案中获取了一些想法并将它们变成了一个Python装饰器@modulize
,它将一个函数转换为一个模块。然后可以照常导入该模块。这是一个例子。
@modulize('my_module')
def my_dummy_function(__name__): # the function takes one parameter __name__
# put module code here
def my_function(s):
print(s, 'bar')
# the function must return locals()
return locals()
# import the module as usual
from my_module import my_function
my_function('foo') # foo bar
装饰器的代码如下
import sys
from types import ModuleType
class MockModule(ModuleType):
def __init__(self, module_name, module_doc=None):
ModuleType.__init__(self, module_name, module_doc)
if '.' in module_name:
package, module = module_name.rsplit('.', 1)
get_mock_module(package).__path__ = []
setattr(get_mock_module(package), module, self)
def _initialize_(self, module_code):
self.__dict__.update(module_code(self.__name__))
self.__doc__ = module_code.__doc__
def get_mock_module(module_name):
if module_name not in sys.modules:
sys.modules[module_name] = MockModule(module_name)
return sys.modules[module_name]
def modulize(module_name, dependencies=[]):
for d in dependencies: get_mock_module(d)
return get_mock_module(module_name)._initialize_
可以找到项目intention。特别是,我为编程竞赛创建了这个,只允许参赛者提交一个.py
文件。这允许我们开发一个包含多个.py文件的项目,然后将它们合并到一个.py
文件中。
答案 4 :(得分:0)
使用 sys.modules
修补 unittest.mock
:
mock.patch.dict(
sys.modules,
{'somefakepackage': mock.Mock()},
)
其他答案正确建议修复 sys.modules
,但正确的方法是使用 mock.patch
修补它。意思是将它暂时(仅在测试运行时)替换为可选地模仿所需行为的假对象。并且在测试完成后恢复它以不影响其他测试用例。
TL;DR 部分中的代码只会使您丢失的包不会引发 ImportError
。要提供包含内容的假包并模仿所需的行为,请使用适当的参数(例如 add attributes via Mock's **kwargs
)启动 mock.Mock(…)
。
下面的代码临时修补 sys.modules
,使其包含 somefakepackage
并使其可从不带 ImportError
的依赖模块导入。
import sys
import unittest
from unittest import mock
class SomeTestCase(unittest.TestCase):
def test_smth(self):
# implement your testing logic, for example:
self.assertEqual(
123,
somefakepackage_dependent.some_func(),
)
@classmethod
def setUpClass(cls): # called once before all the tests
# define what to patch sys.modules with
cls._modules_patcher = mock.patch.dict(
sys.modules,
{'somefakepackage': mock.Mock()},
)
# actually patch it
cls._modules_patcher.start()
# make the package globally visible and import it,
# just like if you have imported it in a usual way
# placing import statement at the top of the file,
# but relying on a patched dependency
global somefakepackage_dependent
import somefakepackage_dependent
@classmethod # called once after all tests
def tearDownClass(cls):
# restore initial sys.modules state back
cls._modules_patcher.stop()
要详细了解 setUpClass
/tearDownClass
方法,请see unittest
docs。
unittest
的内置mock
子包实际上是一个非常强大的工具。更好的 dive deeper into its documentation 以获得更好的理解。