Search code examples
pythonunit-testingpython-mock

How to mock in a python unittest a library not installed locally?


I am trying to test using unittest a python 3.6 script that starts with (my_app.py):

import sys
from awsglue.utils import getResolvedOptions

args = getResolvedOptions(sys.argv, ['opt1', 'opt2', 'opt3'])
opt1 = args['opt1']
opt2 = args['opt2']
opt3 = args['opt3']
....

so in my test I did something like:

import unittest
import datetime
from mock import patch
import my_app

class TestMyApp(unittest.TestCase):

    @patch('awsglue.utils.getResolvedOptions')
    def test_mock_stubs(self, patch_opts):
        patch_opts.return_value = {}
        ....

but the test soon fails at import my_app with:

ModuleNotFoundError: No module named 'awsglue'

as there is not awsglue locally installed. How can I test a module that import a not locally installed library and also mock it in my test?


Solution

  • You'll need to mock the imported module before the import for my_app happens. patch won't work here, because patch imports the module in order to patch it. And in this case, that import itself would cause an error.

    To do this the simplest way is to trick python into thinking that awsglue is already imported. You can do that by putting your mock directly in the sys.modules dictionary. After this you can do the my_app import.

    import unittest
    import sys
    from unittest import mock
    
    
    class TestMyApp(unittest.TestCase):
    
        def test_mock_stubs(self):
            # mock the module
            mocked_awsglue = mock.MagicMock()
            # mock the import by hacking sys.modules
            sys.modules['awsglue.utils'] = mocked_awsglue
            mocked_awsglue.getResolvedOptions.return_value = {}
    
            # move the import here to make sure that the mocks are setup before it's imported
            import my_app
    
    

    You can move the import hack part to a setup fixture method.

    However I would suggest just installing the package. The import hack can be very difficult to maintain.