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 🙂
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 🙂
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 🙂
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 🙂