Python Mocking a function from an imported module

113,927

Solution 1

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.

Solution 2

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')
Share:
113,927

Related videos on Youtube

nsfyn55
Author by

nsfyn55

I've been writing code since the mid 90's, for money since the early 2000's. I have worked for several notable companies including RedHat, Navteq/Nokia, OpenX plus a number of not so notable startups. My first language was C++, but my primary professional experience is in Java, Scala, Python, and PHP. I am a devops enthusiast currently obsessed with Arch linux and LFS. I currently work for a Linode Career Stuff: http://careers.stackoverflow.com/nsfyn55 nsfyn55 at gee male

Updated on December 16, 2021

Comments

  • nsfyn55
    nsfyn55 over 2 years

    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?

    • nsfyn55
      nsfyn55 about 11 years
      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
    nsfyn55 about 11 years
    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.
  • Matti John
    Matti John about 11 years
    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.
  • Mike G
    Mike G over 10 years
    Where did test_patch come from, what is it exactly?
  • Matti John
    Matti John over 10 years
    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.
  • nsfyn55
    nsfyn55 almost 9 years
    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.
  • Aditya
    Aditya over 6 years
    How are you referencing test_method? It will result in error, NameError: global name 'test_method' is not defined
  • Matti John
    Matti John over 6 years
    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.
  • Eats Indigo
    Eats Indigo over 6 years
    Thank you this was very helpful. It had me thrown for a while that you only need to stub the imported callable
  • Kurt Bourbaki
    Kurt Bourbaki over 5 years
    I think this section should be moved on the very top of the docs.
  • kellen
    kellen almost 5 years
    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.
  • Gurce
    Gurce about 3 years
    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.