pythonmockingpython-unittest

How to test if import raises ImportError if package is missing?


I am writing a module which can have pandas as an optional package. The import statement at the top of the file my_submodule.py looks like this.

try:
    import pandas as pd
except (ImportError, ModuleNotFoundError):
    pd = None

Now I want to test that pandas is not installed and either ImportError or ModuleNotFoundError is raised.

How to do this?

At the moment my test file looks like this:

from unittest import TestCase
from unittest.mock import patch

def test_no_pandas_import():

    with patch('sys.path', []):
        from my_module import my_submodule
        assert my_submodule.pd is None

but the assertion is not True, pandas is imported and the errors are not checked.


Solution

  • In my system (where pandas is not installed) I have created exactly your file my_submodule.py, inside the package my_module.

    I have introduced your test function test_no_pandas_import() into the followed test class (in the file test_pandas_exception.py):

    class MyTestCase(TestCase)
    

    So the test code becomes as below:

    from unittest import TestCase, main
    from unittest.mock import patch
    
    class MyTestCase(TestCase):
    
        def test_no_pandas_import(self):
            with patch('sys.path', []):
                # ---------> Note the followed instruction <---------------
                with self.assertRaises(ModuleNotFoundError):
                    from my_module import my_submodule
                    assert my_submodule.pd is None
    
    
    if __name__ == '__main__':
        main()
    

    The main difference between my test code and your test code is that I have added the instruction:

    with self.assertRaises(ModuleNotFoundError):
    

    If I execute the test method test_no_pandas_import() in my system, it pass, but if I remove your instruction:

    with patch('sys.path', []):
    

    the test fails with the following output:

    F
    ======================================================================
    FAIL: test_no_pandas_import (__main__.MyTestCase)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/path/to/stack_overflow/test_pandas_exception.py", line 10, in test_no_pandas_import
        assert my_submodule.pd is None
    AssertionError: ModuleNotFoundError not raised
    
    ----------------------------------------------------------------------
    Ran 1 test in 0.002s
    
    FAILED (failures=1)
    

    In this case the test fails because the Exception ModuleNotFoundError is not raised. The exception is not raised because it has caught inside your file my_submodule.py.

    Removing the exception catch

    If I change the code in your file my_submodule.py so that it doesn't catch the exceptions I obtain the followed code:

    #try:
    import pandas as pd
    #except (ImportError, ModuleNotFoundError):
    #    pd = None
    

    where remains only the import.

    With the previous code I can use the followed test code (without the instruction with patch('sys.path', []):):

    from unittest import TestCase, main
    
    class MyTestCase(TestCase):
    
        def test_no_pandas_import(self):
            with self.assertRaises(ModuleNotFoundError):
                from my_module import my_submodule
                assert my_submodule.pd is None
    
    if __name__ == '__main__':
        main()
    

    If I execute the previous test code in my system (where pandas is not installed) the test pass and this proof that the ModuleNotFoundError is raised.