Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Mock Patch multiple methods in a class

Im trying to patch multiple methods in a class. Here is my simplified set up

Hook.py is defined as

class Hook():
    def get_key(self):
        return "Key"

    def get_value(self):
        return "Value"

HookTransfer.py defined as

from Hook import Hook

class HookTransfer():
    def execute(self):
        self.hook = Hook()
        key = self.hook.get_key()
        value = self.hook.get_value()
        print(key)
        print(value)

I want to mock the methods get_key and get_value in the Hook class. The following works i.e. prints New_Key and New_Value

from HookTransfer import HookTransfer
import unittest
from unittest import mock

class TestMock(unittest.TestCase):
    @mock.patch('HookTransfer.Hook.get_key', return_value="New_Key")
    @mock.patch('HookTransfer.Hook.get_value', return_value="New_Value")
    def test_execute1(self, mock_get_key, mock_get_value):
        HookTransfer().execute()

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

However this does not. It prints <MagicMock name='Hook().get_key()' id='4317706896'> and <MagicMock name='Hook().get_value()' id='4317826128'>

from HookTransfer import HookTransfer
import unittest
from unittest import mock

class TestMock(unittest.TestCase):
    @mock.patch('HookTransfer.Hook', spec=True)
    def test_execute2(self, mock_hook):
        mock_hook.get_key = mock.Mock(return_value="New_Key")
        mock_hook.get_value = mock.Mock(return_value="New_Value")
        HookTransfer().execute()

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

Intuitively it seems like the second one should work too but it doesnt. Could you help explain why it does not. I suspect it has something to do with "where to patch" but Im unable to get clarity.

like image 269
kvb Avatar asked Jan 13 '17 04:01

kvb


People also ask

What is the difference between mock and MagicMock?

So what is the difference between them? MagicMock is a subclass of Mock . It contains all magic methods pre-created and ready to use (e.g. __str__ , __len__ , etc.). Therefore, you should use MagicMock when you need magic methods, and Mock if you don't need them.

What is Side_effect in mock Python?

side_effect: A function to be called whenever the Mock is called. See the side_effect attribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns DEFAULT , the return value of this function is used as the return value.

What is Assert_called_once_with?

assert_called_once_with() is used to check if the method is called with a particular set of arguments.

What is @patch in Python?

This, along with its subclasses, will meet most Python mocking needs that you will face in your tests. The library also provides a function, called patch() , which replaces the real objects in your code with Mock instances.


1 Answers

You can patch multiple methods of a module or a class using patch.multiple(). Something like this should work for your case:

import unittest
from unittest.mock import MagicMock, patch

class TestMock(unittest.TestCase):
    @patch.multiple('HookTransfer.Hook',
                    get_key=MagicMock(return_value='New_Key'),
                    get_value=MagicMock(return_value='New_Value'))
    def test_execute1(self, **mocks):
        HookTransfer().execute()

When patch.multiple() is used as a decorator, the mocks are passed into the decorated function by keyword, and a dictionary is returned when it's used as a context manager.

like image 151
Eugene Yarmash Avatar answered Sep 24 '22 22:09

Eugene Yarmash