python mock patch decorator behaves different for class methods and individual functions

Question:

Several times I’ve ran into a problem with unittest.mock.patch decorator. When I tried to mock individual functions from included module, patch didn’t work. However if functions from included module are collected as class methods, patch works perfectly.

Partially this question intersects with mine. But there is no good answer for this issue as well.

Here is an example of what I’m trying to describe:

|-- __init__.py
|-- helpers.py
|-- main_module.py
|-- tests.py

I wrote one function in helpers.py as a class method, and another one as an individual function:

# helpers.py
class HelperClass():

    def method_a(self):
        return "a"

def function_a():
    return "a"

I’ve included both of them in the main module:

# main_module.py    
from helpers import HelperClass, function_a


    def function_which_uses_helper_function():
        a_val = function_a()
        return a_val

    def function_which_uses_helper_class_method():
        a_val = HelperClass().method_a()
        return a_val

And finally tests:

# tests.py
from unittest import TestCase
from unittest.mock import patch
from main_module import function_which_uses_helper_function, function_which_uses_helper_class_method


class TestClass(TestCase):

    @patch('helpers.function_a')
    def test_function_which_uses_helper_function(self, mock_function_a):
        mock_function_a.return_value = "c"
        self.assertEqual(function_which_uses_helper_function(), "c")

    @patch('helpers.HelperClass.method_a')
    def test_function_which_uses_helper_class_method(self, mock_method_a):
        mock_method_a.return_value = "c"
        self.assertEqual(function_which_uses_helper_class_method(), "c")

Which gives me these results:

$ py.test tests.py
<...>
tests.py .F
<...>
tests.py:11: in test_function_which_uses_helper_function
    self.assertEqual(function_which_uses_helper_function(), "c")
E   AssertionError: 'a' != 'c'
E   - a
E   + c
============ 1 failed, 1 passed in 0.14 seconds ============

I’ll appreciate any help. Hope this also helps someone 🙂

Asked By: AnaPana

||

Answers:

After a while I finally understood why my example for function didn’t work. The explanations is here. And this article was very useful as well. Considering all of that solution will be:

# main_module.py    
import helpers # <- I had to change my import statement


def function_which_uses_helper_function():
    a_val = helpers.function_a()
    return a_val

def function_which_uses_helper_class_method():
    a_val = helpers.HelperClass().method_a()
    return a_val

Another solution is to change the way how I mock my function_a, which is:

from unittest import TestCase
from unittest.mock import patch
from main_module import function_which_uses_helper_function, function_which_uses_helper_class
class TestClass(TestCase):
    
    @patch('main_module.function_a') # <-- !!! I need to mock function in the`main_module`, not in `helpers` !!!
    def test_function_which_uses_helper_function(self, mock_function_a):
        mock_function_a.return_value = "c"
        self.assertEqual(function_which_uses_helper_function(), "c")

    @patch('helpers.HelperClass.function_a')
    def test_function_which_uses_helper_class(self, mock_function_a):
        mock_function_a.return_value = "c"
        self.assertEqual(function_which_uses_helper_class(), "c")

I’m sad that I’ve realized all of that just recently. Hope this helps somebody 🙂

Answered By: AnaPana