Mock a method of a patched class not working

105 Views Asked by At

I need to test a function that uses a lot of legacy code that I can't touch now. I patched the legacy class (LegacyClass) with the @patch decorator on my test class. The legacy class has a method get_dict(args) that returns a dictionary and for my test purpose I need to mock the return of get_dict(args) with a static dictionary.

class_to_test.py


from my_module import LegacyClass

class ClassToTest():
   self.legacy=LegacyClass() #I can't touch this part noe
   
   def method_to_test(self):
        d = self.legacy.get_dict()
        if d['data']['value'] == 1:
            return True
        else:
            return None
               

test.py

@patch('my_module.LegacyClass')
class TestClass(unittest.TestCase):

    def test_case(self, legacy_mock):
        legacy_mock.get_dict = MagicMock(return_value={'data': {'value': 1}})
        
        ctt = ClassToTest()
        assert ctt.method_to_test()

        

In my function, I use the returned dict to check if a specific value is present otherwise I return a default value. The function returns the default value even if the expected value is 1 as in the above example. It seems that the mock of get_dict doesn't work or I do something wrong.

1

There are 1 best solutions below

0
Danila Ganchar On BEST ANSWER

Updated


The main problem is that you trying to mock a property instance after initialization. In your case, the object will always be initialized during import. So the flow looks like:
import ClassToTest -> import LegacyClass -> LegacyClass.__init__ -> mock.patch(...). You need to mock class before initialization if you want to skip all side-effects

Here is an example:

$ tree
# output:
├── src
│   ├── __init__.py
│   ├── class_to_test.py
│   ├── my_module.py
├── test_example.py

src/my_module.py:

class LegacyClass:
    def __init__(self) -> None:
        raise Exception('check side-effects')

    def get_dict(self) -> dict:
        raise NotImplemented()

src/class_to_test.py:

from src.my_module import LegacyClass

class ClassToTest:
    legacy = LegacyClass()

    def method_to_test(self):
        d = self.legacy.get_dict()
        return d['data']['value'] == 1

test_example.py:

from unittest import TestCase, mock


class TestExample(TestCase):
    def test_get_dict(self):
        with mock.patch('src.my_module.LegacyClass'):
            from src.class_to_test import ClassToTest
            to_test = ClassToTest()

            self.assertTrue(isinstance(to_test.legacy, mock.Mock))
            to_test.legacy.get_dict = mock.Mock(return_value={'data': {'value': 1}})

            self.assertTrue(to_test.method_to_test())

Let's check:

pytest test_example.py
============================================================================================= test session starts =============================================================================================
...
collected 1 item                                                                                                                                                                                              

test_example.py .                                                                                                                                                                                       [100%]

============================================================================================== 1 passed in 0.03s ==============================================================================================