Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write unittests for an optional dependency in a python package?

Based on availability of pandas package in working environment a method returns two different outputs :

  • A pandas.DataFrame if pandas is available
  • Otherwise a numpy.recarray object.

How should I write unittest for this class ?

One solution I can think of is to write tests for both cases (with and without pandas installation) and skip test accordingly, something like this:

try:
    import pandas
    HAVE_PANDAS = True
except ImportError:
    HAVE_PANDAS = False

import unittest

class TestClass(unittest.TestCase):
    @unittest.skipUnless(HAVE_PANDAS, "requires pandas")
    def tests_using_pandas(self):
        # do something
    @unittest.skipUnless(not HAVE_PANDAS, "doesn't require pandas")
    def tests_without_pandas(self):
        # do something

But I don't like this solution very much due to decrease in test coverage and skipping tests. I want to run my tests for both cases. It would be helpful if someone can suggest a better alternative solution for this.

like image 529
Utkarsh Gupta Avatar asked Jun 20 '16 06:06

Utkarsh Gupta


People also ask

How do you use assertRaises in Python?

There are two ways you can use assertRaises: using keyword arguments. Just pass the exception, the callable function and the parameters of the callable function as keyword arguments that will elicit the exception. Make a function call that should raise the exception with a context.


1 Answers

If you want to test both cases (which you should), you could possibly force the import of Pandas to fail by adding None to the 'pandas' entry in sys.modules, making sure to add it back again (or delete the entry if it didn't exist in the first place) once the test is done.

import unittest
import sys

class TestWithoutPandas(unittest.TestCase):
    def setUp(self):
        self._temp_pandas = None
        if sys.modules.get('pandas'):
            self._temp_pandas = sys.modules['pandas']
        sys.modules['pandas'] = None

    def tearDown(self):
        if self._temp_pandas:
            sys.modules['pandas'] = self._temp_pandas
        else:
            del sys.modules['pandas']

    def tests_using_pandas(self):
        flag = False
        try:
            import pandas
        except ImportError:
            flag = True
        self.assertTrue(flag)

class TestWithPandas(unittest.TestCase):
    def tests_using_pandas(self):
        flag = False
        try:
            import pandas
        except ImportError:
            flag = True
        self.assertFalse(flag)
like image 77
Andrew Guy Avatar answered Oct 07 '22 00:10

Andrew Guy