我可以在Python中“伪造”一个包(或至少一个模块)以进行测试吗?

32 投票
5 回答
14551 浏览
提问于 2025-04-16 12:33

我想在Python里假装有一个包。我想定义一些东西,这样代码就可以这样写:

from somefakepackage.morefakestuff import somethingfake

而且somefakepackage在代码里被定义了,下面的内容也是如此。这可能吗?这样做的原因是为了让我的单元测试误以为我有一个包(或者像我在标题里说的,一个模块)在Python的路径里,实际上这只是为了这个单元测试假装出来的东西。

5 个回答

8

我从其他回答中获取了一些想法,做成了一个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_

这个项目可以在 GitHub上找到。特别是,我是为了编程比赛而创建这个的,因为比赛只允许参赛者提交一个 .py 文件。这样,你就可以先开发多个 .py 文件,最后再把它们合并成一个 .py 文件。

22

是的,你可以创建一个假的模块:

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
47

当然可以。你可以先定义一个类,把你需要的东西放进去,然后把这个类赋值给 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

撰写回答