1

我想模拟导入模块(ResourceX)的 MyApp,该模块需要当时不可用且无法工作的资源。

一个解决方案是制作并导入 ResourceX 的模拟模块(名为 ResourceXSimulated)并将其作为 ResourceX 转移到 MyApp。我想这样做是为了避免破坏大量代码并从 MyApp 获取各种异常。

我正在使用 Python,它应该类似于:

“将 ResourceX 模拟为 ResourceX 导入”

“ResourceX.getData()”,实际上调用了 ResourceXSimultated.getData()

期待了解 Python 是否支持这种重定向。

干杯。

附加信息:我可以访问源文件。

更新:我正在考虑向 MyApp 添加尽可能少的关于使用 fake 模块的代码,并将此代码添加到 import 语句附近。

4

5 回答 5

4

只需将所有行更改为import ResourceX,并且将行更改为。MyAppimport ResourceXSimulated as ResourceXfrom ResourceX import Yfrom ResourceXSimulated import Y

但是,如果无法访问MyApp源代码或有其他原因不更改它,您可以将模块放入sys.modulesbeforeMyApp加载本身:

import ResourceXSimulated
sys.modules['ResourceX'] = ResourceXSimulated

注意:如果ResourceX是一个包,它可能需要更多的努力。

于 2009-09-18T08:42:26.690 回答
1

这称为猴子补丁,它是 Python 等动态语言中相当广泛使用的技术。

所以大概你有一个类:

class MyOriginal(object):

    def method_x(self):
        do_something_expensive_you_dont_want_in_testing()


obj = MyOriginal()
obj.method_x()

所以在测试中你想做其他事情而不是method_x,但它应该是透明的。因此,您只需利用 Python 的动态语言:

def new_method_x(self):
    pretend_were_doing_something_expensive()

test_obj = MyOriginal()
test_obj.method_x = new_method_x # here's the monkeypatch
test_obj_method_x() # calls the new method
于 2009-09-18T08:35:32.070 回答
1

是的,这是可能的。一些首发:

您可以通过操作 sys.modules 来“转移”模块。它保留了导入模块的列表,您可以在那里使您的模块显示为与原始模块相同的名称。您必须在任何导入您想要伪造的模块的模块之前执行此操作。

您也可以为您的完全不同的模块创建一个名为不同名称的包,但在该包中实际上使用原始模块名称。只要未安装原始模块,此方法就可以正常工作。

在这些情况下,您都不能同时使用这两个模块。为此,您需要对原始模块进行猴子补丁。

当然:完全可以用旧名称调用新模块。但这可能会令人困惑。

于 2009-09-18T08:41:06.583 回答
1

sys.modules正如已经说过的,黑客可以做到这一点。

请注意,如果您可以控制模块ResourceX,那么它自己处理它肯定会更好。这实际上是编写在某些资源存在时工作得更好的模块时的常见模式,例如:

# foo.py
'''A module that provides interface to foo. 

Falls back to a dummy interface if foo is not available.
'''

try:
    from _foo import *
except ImportError:
    from _foo_dummy import *

有时人们以更面向对象的方式来做这件事:

# foo.py
'''A module that provides interface to foo if it exists or to a dummy interface. 

Provides:
    frobnicate()   self-explanatory
    ...
'''

class DummyFoo:
    def frobnicate(self):
        pass
    ...

class UnixFoo(DummyFoo):
    def frobnicate(self):
        a_posix_call()
    ...

class GenericFoo(DummyFoo):
    def frobnicate(self):
        do_something_complicated()
    ...

# Create a default instance.
try:
   if (system == UNIX)
       instance = UnixFoo(system)
   else:
       instance = GenericFoo()
except Exception:
    instance = DummyFoo()

# Now export the public interface.
frobnicate = instance.frobnicate
于 2009-09-18T08:55:00.673 回答
0

是的,Python 可以做到这一点,只要 ResourceXSimulated 模块中公开的方法“看起来和闻起来”像原始模块的这些方法,应用程序应该看不出有什么不同(除了,我假设,假数据填充,不同的响应时间等)。

于 2009-09-18T08:20:33.753 回答