Python Mocking a function from an imported module
Rise to the top 3% as a developer or hire one of them at Toptal: https://topt.al/25cXVn
--------------------------------------------------
Music by Eric Matyas
https://www.soundimage.org
Track title: Dreaming in Puzzles
--
Chapters
00:00 Python Mocking A Function From An Imported Module
00:47 Accepted Answer Score 316
01:26 Answer 2 Score 27
02:01 Answer 3 Score 4
02:41 Answer 4 Score 0
03:26 Thank you
--
Full question
https://stackoverflow.com/questions/1613...
--
Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...
--
Tags
#python #unittesting #pythonunittest #pythonmock
#avk47
ACCEPTED ANSWER
Score 316
When you are using the patch decorator from the unittest.mock package you are patching it in the namespace that is under test (in this case app.mocking.get_user_name), not the namespace the function is imported from (in this case app.my_module.get_user_name).
To do what you describe with @patch try something like the below:
from mock import patch
from app.mocking import test_method
class MockingTestTestCase(unittest.TestCase):
@patch('app.mocking.get_user_name')
def test_mock_stubs(self, test_patch):
test_patch.return_value = 'Mocked This Silly'
ret = test_method()
self.assertEqual(ret, 'Mocked This Silly')
The standard library documentation includes a useful section describing this.
ANSWER 2
Score 27
While Matti John's answer solves your issue (and helped me too, thanks!), I would, however, suggest localizing the replacement of the original 'get_user_name' function with the mocked one. This will allow you to control when the function is replaced and when it isn't. Also, this will allow you to make several replacements in the same test. In order to do so, use the 'with' statment in a pretty simillar manner:
from mock import patch
class MockingTestTestCase(unittest.TestCase):
def test_mock_stubs(self):
with patch('app.mocking.get_user_name', return_value = 'Mocked This Silly'):
ret = test_method()
self.assertEqual(ret, 'Mocked This Silly')
ANSWER 3
Score 4
The accepted answer is correct that using patch you have to consider the namespace in which it is imported. But imagine that you'd want to override the actual implementation on a global basis no matter where it is imported you can monkeypatch the implementation:
@pytest.fixture(autouse=True)
def get_user_name_mock(monkeypatch):
_mock = MagicMock()
monkeypatch.setattr(app.my_module, app.my_module.get_user_name.__name__, _mock )
return _mock
In your tests, just add the name of the fixture as an argument like default fixture behavior.
In my projects I use this to override my global config value resolvement to catch unmocked config.get calls which could cause undefined behavior.
edit: link to docs: https://pytest.org/en/7.3.x/how-to/monkeypatch.html#how-to-monkeypatch-mock-modules-and-environments
ANSWER 4
Score 0
Building up on the answers already given, especially the one by Matti which helped solved my patching issues (I was patching replacing the original function, and not the function call in the module), I prefer to do it without the additional mock library. I am not sure if this 10 years back was shipped as part of the standard Python installation, but today, it is a separate installation and I would much rather keep my pyproject.toml as clean and lightweight as possible. Here's how I solved it with just unnitest.
from app.mocking import test_method
import unittest
from unittest.mock import patch
class MockingTestTestCase(unittest.TestCase):
@patch("app.mocking.get_user_name")
def test_mock_stubs(self, test_patch):
test_patch.return_value = "Mocked This Silly"
ret = test_method()
self.assertEqual(ret, "Mocked This Silly")
self.assertEqual(test_patch.call_count, 1)
I've also added another assertion statement. It's a good idea to make sure calls to specific functions were made. For large code bases with multiple coders, this can be crucial.