How to mock a function which gets executed during the import time?

Question:

Here the ABC() and obj.print_1() get called during the import time and it prints "making object" and "printed 1" respectively. How can we mock all the three functions, __init__(), print_1(), and print_2()?

xyz.py

from abc import ABC
obj = ABC()
obj.print_1()

def func():
   return obj.print_2(2)

abc.py

class ABC():
    def __init__(self):
       print("making object")
       
    def print_1(self):
        print("printed 1")
        return None
    
   def print_2(self, val):
       print("printed ", val)
       return None
Asked By: Amogh Mishra

||

Answers:

Indeed, as soon as you import xyz, it will import abc and create an instance then call a method on it.

Solution : import abc yourself BEFORE xyz EVER GETS IMPORTED, and mock the methods defined in the class. And because we can’t import a method, patch.object is required.

Note : I added a self as parameter in your ABC.print_1 method, otherwise it would be incorrect. Otherwise make it @staticmethod

Here is the test file I used :

import unittest
import unittest.mock as mock

from so74709409_abc import ABC

# no import of `xyz` here !

class Tests(unittest.TestCase):
    def test__xyz_obj_calls_print1(self):
        #                                     __init__ must return None
        with mock.patch.object(ABC, "__init__", **{"return_value": None}) as mock_init, 
             mock.patch.object(ABC, "print_1") as mock_print1, 
             mock.patch.object(ABC, "print_2") as mock_print2:
                from so74709409_xyz import func  # import now !
                func()
        mock_init.assert_called_once()
        mock_print1.assert_called_once_with()
        mock_print2.assert_called_once_with(2)

if __name__ == "__main__":
    unittest.main()

But this is not very robust, if the module was already imported (maybe indirectly) before the test run, the import inside the test won’t have any effect, and so it will fail (mocks not getting called). It can be a pain in a real test suite (with many tests running in sequence) because the previous test will already have imported xyz.

That’s why it’s better to do these kind of things in a if __name__=="__main__", or in a function called deliberately.

(beware : I assume you choose abc as a dummy name, but it is actually a standard library module for Abstract Base Classes)

Answered By: Lenormju