#
# layered_helper.py
#
# Unit test helper for layered charms.
#
import mock
import sys
def mock_modules(parent_module, modules):
'''
I replace a list of modules, inside of a parent module, with mock
objects. You should run me before importing something that you
know will not exist in the test environment, replacing it with a
mock.
I was written to address an issue in writing layered Juju charms,
where some sub modules in the charms.layer module exist in the
source code, but other sub modules will not exist until after the
layered charm is built. If you are dealing with the more
straightforward case where you simply want to mock out an entire
library, you should use more traditional mocking methods.
I return a dictionary, which will be populated with the mock
objects that I have replaced the listed sub modules with. You can
therefore set methods on them, inspect them, etc, just as you
would any mock. I also return the patcher, so that you can call
.stop on it when you no longer need it.
Example usage:
```
from layered_helper import mock_modules
MOCKED_MODULES, PATCHER = mock_modules('charms.layer', ['options'])
```
'''
saved_import = __import__
mocked_modules = {}
def mock_import(name, *args, **kwargs):
if name.startswith(parent_module):
saved_import(parent_module)
for module in modules:
mock_module = mock.Mock()
setattr(sys.modules[parent_module], module, mock_module)
mocked_modules[module] = mock_module
return saved_import(name, *args, **kwargs)
patcher = mock.patch('builtins.__import__', side_effect=mock_import)
patcher.start()
return mocked_modules, patcher