Mocking the __call__ method of a class with unittest.mock.patch

158 Views Asked by At

I have a class (ClassToBeMocked) that is instantiated in the init of another class (ContainerClass). I would like to mock the instantiated ClassToBeMocked and replace its dunder call method.

My code works for regular methods but not for the call dunder method.

from unittest.mock import patch, Mock, MagicMock

class ClassToBeMocked:
    def __call__(self, x):
            return 42

    def regular_method(self, x):
            return 42

class ContainerClass:
    def __init__(self):
        self.runner = ClassToBeMocked()

    def run_a(self):
        return self.runner(x="x")
        
    def run_b(self):
        return self.runner.regular_method(x="x")
    
    
with patch("__main__.ClassToBeMocked") as mock:
    instance = mock.return_value
    instance.__call__ = MagicMock(return_value="Hey")
    instance.regular_method = MagicMock(return_value="Hey")
    
    test = ContainerClass()
    print(test.run_a())
    print(test.run_b())

This outputs :

<MagicMock name='ClassToBeMocked()()' id='140033866116784'>
Hey

While I want to have

Hey
Hey
1

There are 1 best solutions below

0
On BEST ANSWER

In the example below, instance_cls() does not call instance_cls.__call__. It calls type(instance_cls).__call__(instance_cls) as python actually calls the __call__ method defined on the class, not the instance (for more information check this answer on stack overflow).

>>> class ClassToBeMocked:
...     def __call__(self):
...         return 42
...
>>> instance_cls = ClassToBeMocked()
>>> instance_cls() 
... 42

Regarding you example, just change instance.__call__ = MagicMock(return_value="Hey")to instance.__class__.__call__ = MagicMock(return_value="Hey") and it should work.

Full code below :

from unittest.mock import patch, Mock, MagicMock

class ClassToBeMocked:
    def __call__(self, x):
            return 42

    def regular_method(self, x):
            return 42

class ContainerClass:
    def __init__(self):
        self.runner = ClassToBeMocked()

    def run_a(self):
        return self.runner(x="x")
        
    def run_b(self):
        return self.runner.regular_method(x="x")
    
    
with patch("__main__.ClassToBeMocked") as mock:
    instance = mock.return_value
    instance.__class__.__call__ = MagicMock(return_value="Hey")
    instance.regular_method = MagicMock(return_value="Hey")
    
    test = ContainerClass()
    print(test.run_a())  # print "Hey"
    print(test.run_b())  # print "Hey"