views:

74

answers:

5

I want to simulate MyApp that imports a module (ResourceX) which requires a resource that is not available at the time and will not work.

A solution for this is to make and import a mock module of ResourceX (named ResourceXSimulated) and divert it to MyApp as ResourceX. I want to do this in order to avoid breaking a lot of code and get all kinds of exception from MyApp.

I am using Python and It should be something like:

"Import ResourceXSimulated as ResourceX"

"ResourceX.getData()", actually calls ResourceXSimultated.getData()

Looking forward to find out if Python supports this kind of redirection.

Cheers.

ADDITIONAL INFO: I have access to the source files.

UPDATE: I am thinking of adding as little code as possible to MyApp regarding using the fake module and add this code near the import statements.

A: 

Yes, Python can do that, and so long as the methods exposed in the ResourceXSimulated module "look and smell" like these of the original module, the application should not see much any difference (other than, I'm assuming, bogus data fillers, different response times and such).

mjv
I plan to keep the same interface for ResourceXSimulated as ResourceX. Do you also know what is the construction to divert it?
cmdev
Sorry, I don't understand your question. There is no special construct; as you wrote it in the question is it: import X as Y and then Y.DoThis() etc.
mjv
+1  A: 

This is called monkey-patching, and it's a fairly widely-used technique in dynamic languages like Python.

So presumably you have a class:

class MyOriginal(object):

    def method_x(self):
        do_something_expensive_you_dont_want_in_testing()


obj = MyOriginal()
obj.method_x()

so in testing you want to do something else instead of method_x, but it should be transparent. So you just take advantage of Python's dynamic language:

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
Daniel Roseman
Thank you Daniel for the code snippet. I have a few comments with this construct. This is a great way to mock/fake the functions within the same class. But I want to fake the module at a higher level of abstraction to keep a looser coupling between the mock and the actual module. I wonder if I can use your idea to divert not the functions but to divert the module.
cmdev
+1  A: 

Yes, it's possible. Some starters:

You can "divert" modules by manipulating sys.modules. It keeps a list of imported modules, and there you can make your module appear under the same name as the original one. You must do this manipulating before any module that imports the module you want to fake though.

You can also make a package called a different name, but in that package actually use the original module name, for your completely different module. This works well as long as the original module isn't installed.

In none of these cases you can use both modules at the same time. For that you need to monkey-patch the original module.

And of course: It' perfectly possible to just call the new module with the old name. But it might be confusing.

Lennart Regebro
+3  A: 

Just change all lines import ResourceX in MyApp to import ResourceXSimulated as ResourceX, and lines like from ResourceX import Y to from ResourceXSimulated import Y.

However if don't have access to MyApp source or there are other reasons not to change it, you can put your module into sys.modules before MyApp is loaded itself:

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

Note: if ResourceX is a package, it might require more effort.

Denis Otkidach
+1  A: 

It's possible with the sys.modules hack, as already said.

Note that if you have control over module ResourceX it's certainly better that it takes care of it itself. This is actually a common pattern when writing modules that work better when some resource is present, e.g.:

# 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 *

Sometimes people do it in a more object-oriented way:

# 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
ilya n.