223

I want to understand how to @patch a function from an imported module.

This is where I am so far.

app/mocking.py:

from app.my_module import get_user_name

def test_method():
  return get_user_name()

if __name__ == "__main__":
  print "Starting Program..."
  test_method()

app/my_module/__init__.py:

def get_user_name():
  return "Unmocked User"

test/mock-test.py:

import unittest
from app.mocking import test_method 

def mock_get_user():
  return "Mocked This Silly"

@patch('app.my_module.get_user_name')
class MockingTestTestCase(unittest.TestCase):

  def test_mock_stubs(self, mock_method):
    mock_method.return_value = 'Mocked This Silly')
    ret = test_method()
    self.assertEqual(ret, 'Mocked This Silly')

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

This does not work as I would expect. The "patched" module simply returns the unmocked value of get_user_name. How do I mock methods from other packages that I am importing into a namespace under test?

bennylope
  • 1,113
  • 2
  • 13
  • 24
nsfyn55
  • 14,875
  • 8
  • 50
  • 77
  • I am asking if I am going about this right. I looked at Mock, but I don't see a way to solve this particular problem. Is there a way to recreate what I did above in Mock? – nsfyn55 Apr 21 '13 at 18:00

4 Answers4

295

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.

Voy
  • 5,286
  • 1
  • 49
  • 59
Matti John
  • 19,329
  • 7
  • 41
  • 39
  • 1
    this gets to my problem. `get_user_name` is in a different module than `test_method`. Is there a way to mock something in a sub_module? I fixed it in an ugly way below. – nsfyn55 Apr 21 '13 at 19:11
  • 7
    It doesn't matter that `get_user_name` is in a different module than `test_method` since you are importing the function into `app.mocking` they are in the same namespace. – Matti John Apr 21 '13 at 19:22
  • 4
    Where did test_patch come from, what is it exactly? – Mike G Nov 16 '13 at 06:12
  • 3
    test_patch is passed in by the patch decorator and is the mocked get_user_name object (i.e. an instance of the MagicMock class). It might be clearer if it was named something like `get_user_name_patch`. – Matti John Nov 17 '13 at 22:37
  • How are you referencing test_method? It will result in error, NameError: global name 'test_method' is not defined – Aditya Oct 27 '17 at 08:35
  • `test_method` is just a placeholder for the function being tested, in the question it's imported. I have added the import to match the example code in the question. – Matti John Oct 27 '17 at 11:22
  • Thank you this was very helpful. It had me thrown for a while that you only need to stub the imported callable – Eats Indigo Dec 04 '17 at 13:55
  • I think this section should be moved on the very top of the docs. – Kurt Bourbaki Jan 08 '19 at 13:09
  • Thank you very much! Despite having read the "Where to patch" section of the docs, I didn't fully grok it until I read your answer. – kellen May 17 '19 at 16:32
  • 2
    One little caveat that I tripped over just now was that the "from mock import patch" line *must* be the first line. If you import test_method first, and then import patch, it won't work. Just wanted to highlight that. – Gurce Feb 24 '21 at 23:48
26

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')
Tgilgul
  • 1,614
  • 1
  • 20
  • 35
  • 19
    This is sort of immaterial to the posed question. Whether you use `patch` as a decorator or context manager is specific to the use case. For instance you can use `patch` as a decorator to mock a value for all tests in an `xunit` or `pytest` class while in other cases its useful to have the fine grained control afforded by the context manager. – nsfyn55 Jul 20 '15 at 17:35
3

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

Emptyless
  • 2,964
  • 3
  • 20
  • 30
-3

Besides Matti John's solution, you can also import module instead of function in app/mocking.py.

# from app.my_module import get_user_name
from app import my_module

def test_method():
  return my_module.get_user_name()

if __name__ == "__main__":
  print "Starting Program..."
  test_method()
lyh543
  • 847
  • 6
  • 8
  • Why does this get downvoted? Changing the way things get imported has its disadvantages but I can think of many scenarios in which this might actually be better than patching at importing modules. – Michał Jabłoński Jun 15 '23 at 19:20
  • I think it's not clear how this answers the question. This is just a semantic alternative for addressing the function. At the heart of the question was "Why does this mock not work as expected?" your answer doesn't answer that question. – nsfyn55 Jul 10 '23 at 15:09