Python: Mock a module without importing it or needing it to exist
I had a similar problem where the helpers
library couldn't be loaded as it needed special hardware.
Rather than make radical changes to the code that you want to test, an alternative is to insert a "fake" directory into sys.path
as suggested by how to add a package to sys path for testing
import os, sys
fake_dir = os.path.join(os.path.dirname(__file__), 'fake')
assert(os.path.exists(fake_dir))
sys.path.insert(0, fake_dir)
import foo
from unittest.mock import sentinel
def foo_test():
foo.helpers.helper_func.return_value = sentinel.foobar
assert foo.foo_func() == sentinel.foobar
where fake
is structured as:
.
├── fake/
│ └── helpers/
│ └── __init__.py
├── foo.py
└── helpers/
and __init__.py
has
from unittest.mock import Mock
helper_func = Mock()
You're kind of missing the point of what a Mock is. You're supposed to build them when you want an object with a particular interface, regardless of how it's implemented.
What you're doing is trying to re-implement python's module system, plus it's a pretty horrible abuse of global variables to boot.
Instead of making foo a module, make a Foo class, and pass in the helpers in the constructor.
class Foo(object):
def __init__(self, helpers):
self.helpers = helpers
# then, instead of import foo:
foo = Foo(mock_helpers)
Even if the real "helpers" is actually going to be a module, there is no reason you need to be messing with sys.modules and setting it up via import
in your tests.
And if foo has to be a module, that's fine too, but you do it like this:
# foo.py
class Foo(object):
pass # same code as before, plus foo_func
try:
import whatever
_singleton = Foo(whatever)
except ImportError:
_singleton = Foo(something_else)
def foo_func():
return _singleton.foo_func()
Large chunks of the standard library work this way. It's pretty much the standard for defining singleton-like modules.