0

I have the following situation.

class Class1():
    def __init__(self, param1):
        self.param = param1
    def my_func_1(self):
        return "Hello " + self.param1

class Class2():
    def __init__(self):
        self.instance_of_class_1 = Class1('Real')

    def do_it(self):
        return self.instance_of_class_1.my_func_1()

class Class3():
    def __init__(self):
        self.instace_of_class_2 = Class2()

    def do_it(self):
        return self.instace_of_class_2.do_it()

I have a test that initiates a Class3 object but I want to mock the new instance of Class1 inside the constructor of Class2. This is what i did so far:

def test_my_classes():
    with patch('my.module.Class1') as class_1_mock:
        class_1_mock.my_func_1.return_value = "Hello Fake"
        class_3 = Class3()
        assert class_3.do_it() == 'Hello Fake' #fails

I'm guessing because Class1 constructor takes params - its not a simple patch.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Mr T.
  • 4,278
  • 9
  • 44
  • 61

1 Answers1

0

You are mocking Class1 correctly, but forgot to take into account it is called to produce an instance. The my_func_1 would be looked up on the call result (on the instance):

def test_my_classes():
    with patch('my.module.Class1') as class_1_mock:
        class_1_mock.return_value.my_func_1.return_value = "Hello Fake"

Mock doesn't care that you produced an instance or that my_func_1 is a bound method, only that you call Class1.

You can then make assertions if the class was called with the correct parameters, if you so desire:

class_1_mock.assert_called_once_with('Real')

Demo:

>>> from mock import patch
>>> class Class1():
...     def __init__(self, param1):
...         self.param = param1
...     def my_func_1(self):
...         return "Hello " + self.param1
...
>>> class Class2():
...     def __init__(self):
...         self.instance_of_class_1 = Class1('Real')
...     def do_it(self):
...         return self.instance_of_class_1.my_func_1()
...
>>> class Class3():
...     def __init__(self):
...         self.instace_of_class_2 = Class2()
...     def do_it(self):
...         return self.instace_of_class_2.do_it()
...
>>> with patch('__main__.Class1') as class_1_mock:
...     class_1_mock.return_value.my_func_1.return_value = "Hello Fake"
...     class_3 = Class3()
...     print class_3.do_it() == 'Hello Fake'
...
True
>>> class_1_mock.mock_calls
[call('Real'), call().my_func_1()]

I included class_1_mock.mock_calls to show that call().my_func_1() is included; that's the Class1('Real') instance being used to provide the my_func_1() method call.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • When i debug the test and get to the line of initialization of Class1 then i see its of type `my.module.Class1` and not `MagicMock` - This is not the wanted affect. – Mr T. Nov 07 '16 at 09:22
  • 1
    @MrT.: are you certain you [patched the right location](https://docs.python.org/3/library/unittest.mock.html#where-to-patch) then? – Martijn Pieters Nov 07 '16 at 09:39
  • 1
    OK - now i understand my problem. I used the module where the class was defined and not where the class was used! Thanks :) – Mr T. Nov 07 '16 at 09:47