Mock an entire module in python

TzurEl picture TzurEl · Dec 19, 2016 · Viewed 7.2k times · Source

I have an application that imports a module from PyPI. I want to write unittests for that application's source code, but I do not want to use the module from PyPI in those tests.
I want to mock it entirely (the testing machine will not contain that PyPI module, so any import will fail).

Currently, each time I try to load the class I want to test in the unittests, I immediately get an import error. so I thought about maybe using

try: 
    except ImportError:

and catch that import error, then use command_module.run(). This seems pretty risky/ugly and I was wondering if there's another way.

Another idea was writing an adapter to wrap that PyPI module, but I'm still working on that.

If you know any way I can mock an entire python package, I would appreciate it very much. Thanks.

Answer

Don Kirkby picture Don Kirkby · Dec 19, 2016

If you want to dig into the Python import system, I highly recommend David Beazley's talk.

As for your specific question, here is an example that tests a module when its dependency is missing.

bar.py - the module you want to test when my_bogus_module is missing

from my_bogus_module import foo

def bar(x):
    return foo(x) + 1

mock_bogus.py - a file in with your tests that will load a mock module

from mock import Mock
import sys
import types

module_name = 'my_bogus_module'
bogus_module = types.ModuleType(module_name)
sys.modules[module_name] = bogus_module
bogus_module.foo = Mock(name=module_name+'.foo')

test_bar.py - tests bar.py when my_bogus_module is not available

import unittest

from mock_bogus import bogus_module  # must import before bar module
from bar import bar

class TestBar(unittest.TestCase):
    def test_bar(self):
        bogus_module.foo.return_value = 99
        x = bar(42)

        self.assertEqual(100, x)

You should probably make that a little safer by checking that my_bogus_module isn't actually available when you run your test. You could also look at the pydoc.locate() method that will try to import something, and return None if it fails. It seems to be a public method, but it isn't really documented.