pythonpytestpytest-mockpytest-mock-patch

pytest @patch does not work for mocking the whole class (all the methods of the class) but works for mocking individual methods of the class


I have two classes like below:

in parent.py:

class TestClassParent(ABC):
    def test_method(self):
        print('the method TestMethod is called')
        return True

in child.py:

from src.scripts.parent import TestClassParent
class TestClassChild(TestClassParent):
    custom_vars = [1, 2, 3]

I have a test class for child.py called, child_test.py like this:

from unittest import TestCase
from unittest.mock import patch
from src.scripts.child import TestClassChild
class ChildTest(TestCase):
    @patch('src.scripts.child.TestClassParent', autospec=True)
    def test(self, mock_parent_class):
        mock_parent_class.return_value.test_method.return_value = False
        i = TestClassChild()
        a = i.test_method()
        assert a is False

Looks like the patch @patch('src.scripts.child.TestClassParent', autospec=True) didn't work and when a = i.test_method() is executed, the actual implementation is run.

However mocking an individual methods of the TestClassParent works as expected for example if I change the implementation like below, it works fine:

@patch('src.scripts.child.TestClassParent.test_method', autospec=True)
    def test(self, mock_method):
        mock_method.return_value = False
        i = TestClassChild()
        a = i.test_method()
        assert a is False

I also read from pytest - Patching a class does not work, calls class instead that I should mock the class where it's used not where it's defined and I did it exactly like that, the TestClassParent was used inside TestClassChild so I mocked where it's used: @patch('src.scripts.child.TestClassParent') but no luck. I even mocked both TestClassChild and TestClassParent (both the usage and definition) still no luck and also removed autospec=True but again no luck.

Any ideas ??

Any help would be hugely appreciated.


Solution

  • I think you may be assuming an incorrect resolution order.

    The modules are imported and therefore the class relationships defined before the patch ever occurs.

    What you're targeting by mocking all methods of a parent class that propagate through children can probably be done through meta programming, although it would likely be sort of hacky -- so I wouldn't recommend it.

    Another solution could be to alter the classes to utilize dependency injection, but if it were me, I would simply mock the instances as needed and call it a day.

    This link may provide further clarification -- https://stackoverflow.com/a/38928265/13261176