Mocking a failing module import in Python

Published: Feb. 11, 2021

When testing Python code, one sometimes has to imitate a broken or missing module. In a specific example, the pywin32 package requires running a post-installation script with administrator privileges after initial installation. Importing, for example, the win32security module of pywin32 may throw an ImportError if the postinstallation script wasn't executed. Packages that depend on pywin32 may want to handle this situation gracefully so that users who just pip install mypackage get only a soft failure or at least a helpful error message. If one wants to test this behavior in continuous integration and get code coverage, the ImportError has to be triggered even if pywin32 is installed correctly. Here is a short example that mimicks a broken or missing package with pytest and monkeypatch, since I didn't find this elsewhere:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import sys
import builtins

import pytest


real_import = builtins.__import__


def monkey_import_notfound(name, globals=None, locals=None, fromlist=(), level=0):
    if name in ('win32security', 'pywintypes'):
        raise ModuleNotFoundError(f"Mocked module not found {name}")
    return real_import(name, globals=globals, locals=locals, fromlist=fromlist, level=level)


def monkey_import_importerror(name, globals=None, locals=None, fromlist=(), level=0):
    if name in ('win32security', ):
        raise ImportError(f"Mocked import error {name}")
    return real_import(name, globals=globals, locals=locals, fromlist=fromlist, level=level)


def test_import_selftest(monkeypatch):
    monkeypatch.delitem(sys.modules, 'win32security', raising=False)
    monkeypatch.setattr(builtins, '__import__', monkey_import_importerror)

    with pytest.raises(ImportError):
        import win32security


def test_import_selftest2(monkeypatch):
    monkeypatch.delitem(sys.modules, 'win32security', raising=False)
    monkeypatch.setattr(builtins, '__import__', monkey_import_notfound)

    with pytest.raises(ModuleNotFoundError):
        import win32security


def test_import_broken(monkeypatch):
    monkeypatch.delitem(sys.modules, 'win32security', raising=False)
    monkeypatch.delitem(sys.modules, 'my_module_using_win32security', raising=False)
    monkeypatch.setattr(builtins, '__import__', monkey_import_importerror)

    # Example: handle error with a fallback function with reduced capabilities
    from my_module_using_win32security import my_function, my_graceful_fallback

    assert my_function is my_graceful_fallback


def test_import_missing(monkeypatch):
    monkeypatch.delitem(sys.modules, 'win32security', raising=False)
    monkeypatch.delitem(sys.modules, 'my_module_using_win32security', raising=False)
    monkeypatch.setattr(builtins, '__import__', monkey_import_notfound)

    # Example: handle missing module with a fallback function with reduced capabilities
    from my_module_using_win32security import my_function, my_graceful_fallback

    assert my_function is my_graceful_fallback

Comments: Be first to comment!

Comments

No comments yet.

Post your comment