7

I have a test that makes sure a specific (helpful) error message is raised, when a required package is not available.

def foo(caller):
    try:
        import pkg
    except ImportError:
        raise ImportError(f'Install "pkg" to use {caller}')
    pkg.bar()

with pytest.raises(ImportError, match='Install "pkg" to use test_function'):
   foo('test_function')

However, pkg is generally available, as other tests rely on it. Currently, I set up an additional virtual env without pkg just for this test. This seems like overkill.

Is it possible to "hide" an installed package within a module or function?

rvf
  • 1,409
  • 2
  • 15
  • 21
  • 3
    Does this answer your question? [How to mock an import](https://stackoverflow.com/questions/8658043/how-to-mock-an-import) – Capie Feb 14 '20 at 13:48
  • 1
    @Capie I think mocking does the contrary? As in: provide something that is not there, versus: hide something that is there. Or am I overlooking something? In any case, I will clarify the question. – rvf Feb 14 '20 at 14:12
  • well, i see you tagged pytest, so I assume you are trying to tests something. Mocking the import, you can set it to the value you want, be `module not found` in this case. That's how you do on test, so you can run them wherever without having to worry the state of the host machine. – Capie Feb 14 '20 at 14:22
  • 1
    Similar question: https://stackoverflow.com/q/51044068/2650249 – hoefling Feb 14 '20 at 14:44

2 Answers2

6

I ended up with the following pytest-only solution, which appears to be more robust in the setting of a larger project.

import builtins
import pytest


@pytest.fixture
def hide_available_pkg(monkeypatch):
    import_orig = builtins.__import__

    def mocked_import(name, *args, **kwargs):
        if name == 'pkg':
            raise ImportError()
        return import_orig(name, *args, **kwargs)

    monkeypatch.setattr(builtins, '__import__', mocked_import)


@pytest.mark.usefixtures('hide_available_pkg')
def test_message():
    with pytest.raises(ImportError, match='Install "pkg" to use test_function'):
        foo('test_function')
rvf
  • 1,409
  • 2
  • 15
  • 21
3

You can mock builtins.__import__.

from unittest import mock

import pytest


def foo(caller):
    try:
        import pkg
    except ImportError:
        raise ImportError(f'Install "pkg" to use {caller}')
    pkg.bar()

with mock.patch("builtins.__import__", side_effect=ImportError):
    with pytest.raises(ImportError, match='Install "pkg" to use test_function'):
        foo('test_function')
Łukasz Rogalski
  • 22,092
  • 8
  • 59
  • 93
  • Thanks, this works for the snippet I provided, so +1. There appear to be side effects, when additional imports happen, so I take a slightly different approach. – rvf Feb 14 '20 at 15:25