3

I'm trying to reach 100% testing coverage in a bit of code that I'm writing. The following block of code, however, is giving me trouble.

try:
    from south.modelsinspector import add_introspection_rules
    add_introspection_rules([], ["^localized_recurrence\.duration_field\.DurationField"])
except ImportError:
    pass

The code above is part of my module under test. I need to create a test (without modifying the code above) which follows the ImportError branch.

How can I programmatically cause the ImportError to occur, while only writing code in my tests?

Wilduck
  • 13,822
  • 10
  • 58
  • 90
  • 4
    What about `raise ImportError`? – Paulo Bu Mar 25 '14 at 14:38
  • I should be clear, The code above is the code under test, I want to cause the error without modifying that code, but rather from a test. I will edit my question accordingly. – Wilduck Mar 25 '14 at 14:42
  • 1
    I understand, you want to force that code to fail with `ImportError`. – Paulo Bu Mar 25 '14 at 14:43
  • I've used this in my unit tests, http://docs.python.org/2/library/unittest.html#unittest.TestCase.assertRaises –  Mar 25 '14 at 14:46
  • Edgar: that tests that the code raises an exception, I want the opposite, I want to force it to be raised in the block above. – Wilduck Mar 25 '14 at 14:47
  • possible duplicate of [Mocking ImportError in Python](http://stackoverflow.com/questions/2481511/mocking-importerror-in-python) – Sjoerd Mar 25 '14 at 14:51

2 Answers2

6

I'd try patching sys.modules and replacing south.modelsinspector with a mock module.

See the docs on Import statement for inspiration.

In [1]: from re import sub

In [2]: import sys

In [3]: sys.modules['re'] = {}

In [4]: from re import sub
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
/home/kos/codility/frontend_v2/<ipython-input-4-6d4794835d43> in <module>()
----> 1 from re import sub

ImportError: cannot import name sub

You can do it in a narrow context by using mock.patch.dict (as a test decorator or context manager):

In [6]: with mock.patch.dict('sys.modules', {'re': {}}):
            from re import sub
   ...:     
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-6-7479025ab931> in <module>()
      1 with mock.patch.dict('sys.modules', {'re': {}}):
----> 2     from re import sub
      3 

ImportError: cannot import name sub

In [8]: from re import sub

In [9]: 
Kos
  • 70,399
  • 25
  • 169
  • 233
  • This is exactly what I'm looking for. I'm going to give this a shot. – Wilduck Mar 25 '14 at 14:48
  • 1
    I spent a significant amount of time trying to get this to work. It eventually did, but I needed to use `reload(my_module_under_test)` from within the patch context manager for the patch to be applied on import of that file. With that additional detail (feel free to edit it into this answer), I'm accepting this as the correct answer for my problem. Thanks so much Kos! – Wilduck Mar 25 '14 at 20:20
3

You can change sys.path for the test. For example:

>>>import bs4
>>>

>>>import sys
>>>p=sys.path
>>>sys.path=['']
>>>import bs4

ImportError: No module named bs4

>>>sys.path=p
>>>import bs4
>>>

Just modify sys.path for that specific test on setUp() and later on tearDown() restore it.

Hope this helps!

Paulo Bu
  • 29,294
  • 6
  • 74
  • 73