Search code examples
pythonunit-testingrefactoringpython-import

Python, unit-testing and mocking imports


I am in a project where we are starting refactoring some massive code base. One problem that immediately sprang up is that each file imports a lot of other files. How do I in an elegant way mock this in my unit test without having to alter the actual code so I can start to write unit-tests?

As an example: The file with the functions I want to test, imports ten other files which is part of our software and not python core libs.

I want to be able to run the unit tests as separately as possible and for now I am only going to test functions that does not depend on things from the files that are being imported.

EDIT

Thanks for all the answers.

I didn't really know what I wanted to do from the start but now I think I know.

Problem was that some imports was only possible when the whole application was running because of some third-party auto-magic. So I had to make some stubs for these modules in a directory which I pointed out with sys.path

Now I can import the file which contains the functions I want to write tests for in my unit-test file without complaints about missing modules.


Solution

  • If you want to import a module while at the same time ensuring that it doesn't import anything, you can replace the __import__ builtin function.

    For example, use this class:

    class ImportWrapper(object):
        def __init__(self, real_import):
            self.real_import = real_import
    
        def wrapper(self, wantedModules):
            def inner(moduleName, *args, **kwargs):
                if moduleName in wantedModules:
                    print "IMPORTING MODULE", moduleName
                    self.real_import(*args, **kwargs)
                else:
                    print "NOT IMPORTING MODULE", moduleName
            return inner
    
        def mock_import(self, moduleName, wantedModules):
            __builtins__.__import__ = self.wrapper(wantedModules)
            try:
                __import__(moduleName, globals(), locals(), [], -1)
            finally:
                __builtins__.__import__ = self.real_import
    

    And in your test code, instead of writing import myModule, write:

    wrapper = ImportWrapper(__import__)
    wrapper.mock_import('myModule', [])
    

    The second argument to mock_import is a list of module names you do want to import in inner module.

    This example can be modified further to e.g. import other module than desired instead of just not importing it, or even mocking the module object with some custom object of your own.