Python unit tests – mocking imported class methods

Question:

I would like to mock some imported class methods and module functions for my unit tests. I tried several ways to define the mocked values but I don’t understand why they are not taken into account.

I wrote some tests following the advices in Python Mocking a function from an imported module.

Here is a piece of code representing the application to test:

from services import myModule1
from services.spec1 import importedClass

class myClass(object):
    def __init__(self, param1, param2):
        self.param1 = param1
        self.param2 = param2
        self.param3 = 0
        self.param4 = 0
        
        self.myMethod()
    
    def myMethod(self):
        newVar = importedClass()
        self.param3 = newVar.meth1(self.param2)
        
        calcParam = myModule1.methodMod1(self.param1)
        self.param4 = calcParam["keyParam3"]

I would like to unit test myMethod and I need to mock importedClass and myModule1.methodMod1().

Here is a new piece of code that I tried for the tests (previous attempts below):

import unittest
from unittest.mock import patch

from my_module import myClass

class test_myClass(unittest.TestCase):
    @patch('my_module.importedClass')
    @patch('my_module.myModule1')
    def test_myMethod(self, mock_mod1, mock_class):
        mock_mod1.methodMod1.return_value = {"keyParam3": 5, "keyParam4": 7}
        mock_class.meth1.return_value = 2
        test_parameters = (0, 0)
        test_res = myClass(*test_parameters)
        self.assertEqual(test_res.param3, 2)
        self.assertEqual(test_res.param4, 5)

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

The mocking has no error but the mocked values are not taken into account.

Previous attempts

What I tried for each of them, using 2 different approaches:

import unittest
from unittest.mock import Mock, patch

from my_module import myClass

import services

class test_myClass(unittest.TestCase):
    def setUp(self):
        services.spec1 = Mock()
        services.spec1.importedClass.meth1.return_value = 2

    @patch('services.myModule1')
    def test_myMethod(self, my_mock):
        my_mock.methodMod1.return_value = {"keyParam3": 5, "keyParam4": 7}
        test_parameters = (0, 0)
        test_res = myClass(*test_parameters)
        self.assertEqual(test_res.param3, 2)
        self.assertEqual(test_res.param4, 5)

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

The result is that the calculated attributes are not updated and still 0 – so test fails.

I also tried with services = Mock() and defined return values for each part, to regroup each mock in setUp method or in a @patch, but nothing worked.

I also tried with my_module.spec1 = Mock(), to make the function global, or even self.spec1 = Mock() to make it very local to the test’s context (if I understood correctly the differences, this is something I’m not really sure neither) but nothing worked.

Asked By: Christophe

||

Answers:

To mock just the method:

class test_myClass(unittest.TestCase):
    # @patch('my_module.importedClass')      # Change this
    @patch('my_module.importedClass.meth1')  # to this
    @patch('my_module.myModule1')
    # def test_myMethod(self, mock_mod1, mock_class):      # Change this
    def test_myMethod(self, mock_mod1, mock_class_meth1):  # to this
        mock_mod1.methodMod1.return_value = {"keyParam3": 5, "keyParam4": 7}
        # mock_class.meth1.return_value = 2  # Change this
        mock_class_meth1.return_value = 2    # to this
        test_parameters = (0, 0)
        test_res = myClass(*test_parameters)
        self.assertEqual(test_res.param3, 2)
        self.assertEqual(test_res.param4, 5)

To mock the class and its method:
https://docs.python.org/3/library/unittest.mock-examples.html#mocking-classes

class test_myClass(unittest.TestCase):
    @patch('my_module.importedClass')
    @patch('my_module.myModule1')
    def test_myMethod2(self, mock_mod1, mock_class):
        mock_mod1.methodMod1.return_value = {"keyParam3": 5, "keyParam4": 7}
        # mock_class.meth1.return_value = 2            # Change this
        mock_class_instance = mock_class.return_value  # to these
        mock_class_instance.meth1.return_value = 2     # two lines
        test_parameters = (0, 0)
        test_res = myClass(*test_parameters)
        self.assertEqual(test_res.param3, 2)
        self.assertEqual(test_res.param4, 5)
Answered By: aaron
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.