108

I am trying to Mock a function (that returns some external content) using the python mock module.

I'm having some trouble mocking functions that are imported into a module.

For example, in util.py I have

def get_content():
  return "stuff"

I want to mock util.get_content so that it returns something else.

I am trying this:

util.get_content=Mock(return_value="mocked stuff")

If get_content gets invoked inside another module, it never actually seems to return the mocked object. Am I missing something in terms of how to use Mock?

Note that if I invoke the following, things work correctly:

>>> util.get_content=Mock(return_value="mocked stuff")
>>> util.get_content()
"mocked stuff"

However, if get_content is called from inside another module, it invokes the original function instead of the mocked version:

>>> from mymodule import MyObj
>>> util.get_content=Mock(return_value="mocked stuff")
>>> m=MyObj()
>>> m.func()
"stuff"

Contents of mymodule.py

from util import get_content

class MyObj:    
    def func():
        get_content()

So I guess my question is - how do I get invoke the Mocked version of a function from inside a module that I call?

It appears that the from module import function may be to blame here, in that it doesn't point to the Mocked function.

Daniel Holmes
  • 1,952
  • 2
  • 17
  • 28
shreddd
  • 10,975
  • 9
  • 33
  • 34
  • I followed your exact description (in python 2.5, with mock 0.7.0, on linux), and it worked fine. Do you have any more details that you can provide? – Nate Mar 13 '11 at 00:45
  • Hmm - it looks like the function behaves as expected when it is called from the top level scope. However, when it gets called from inside another module or function (i.e. further down the call stack) it doesn't exhibit the Mock-ed behavior. I am clarifying the example to illustrate this. – shreddd Mar 13 '11 at 03:12
  • Okay, I tried your new description - I'm **still** getting the correct answer, even from `mymodule.func()`. The only difference for me was that my `mymodule.func()` `return`s `util.get_content()`, and doesn't just call it. I feel like there must still be some information missing in your description. Have you actually tried your exact description above? What is your *actual* code? – Nate Mar 13 '11 at 11:46
  • Sorry about that - I was avoiding having to paste large sections of code. You are correct - my reductive example doesn't quite fail, but I think I've partially figured out a solution. Will update this. – shreddd Mar 14 '11 at 05:30
  • Strange - things aren't working right in my more complex Django test case, but when I try to distill it down the Mock object seems to be getting passed around as expected. I'm guessing there is some difference in the imports which is creating slightly different namespaces. – shreddd Mar 14 '11 at 06:15

5 Answers5

58

The general case would be to use patch from mock. Consider the following:

utils.py

def get_content():
    return 'stuff'

mymodule.py

from util import get_content


class MyClass(object):

    def func(self):
        return get_content()

test.py

import unittest

from mock import patch

from mymodule import MyClass

class Test(unittest.TestCase):

    @patch('mymodule.get_content')
    def test_func(self, get_content_mock):
        get_content_mock.return_value = 'mocked stuff'

        my_class = MyClass()
        self.assertEqual(my_class.func(), 'mocked stuff')
        self.assertEqual(get_content_mock.call_count, 1)
        get_content_mock.assert_called_once()

Note how get_content is mocked, it is not util.get_content, rather mymodule.get_content since we are using it in mymodule.

Above has been tested with mock v2.0.0, nosetests v1.3.7 and python v2.7.9.

KanAfghan
  • 752
  • 5
  • 9
46

I think I have a workaround, though it's still not quite clear on how to solve the general case

In mymodule, if I replace

from util import get_content

class MyObj:    
    def func():
        get_content()

with

import util

class MyObj:    
    def func():
        util.get_content()

The Mock seems to get invoked. It looks like the namespaces need to match (which makes sense). However, the weird thing is that I would expect

import mymodule
mymodule.get_content = mock.Mock(return_value="mocked stuff")

to do the trick in the original case where I am using the from/import syntax (which now pulls in get_content into mymodule). But this still refers to the unmocked get_content.

Turns out the namespace matters - just need to keep that in mind when writing your code.

Daniel Holmes
  • 1,952
  • 2
  • 17
  • 28
shreddd
  • 10,975
  • 9
  • 33
  • 34
  • 2
    I'd really love the answer for the general case. Sometimes `from util import get_context` syntax works with mocking and sometimes it doesn't. Anyone have an explanation? – johnboiles Jan 30 '14 at 01:01
  • 1
    Thank you very much for your answer. I was very confused, why my mock worked in some files and not in others. – guettli Nov 03 '14 at 10:04
  • 4
    @johnboiles the answer to "why it works sometimes, sometimes not": The order of imports does matter here. If you mock gets executed before the import it works, if the import gets executed first, then it does not work. – guettli Jan 14 '16 at 08:27
29

You have to patch the function where it is being used. In your case that would be in the mymodule module.

import mymodule
>>> mymodule.get_content = Mock(return_value="mocked stuff")
>>> m = mymodule.MyObj()
>>> m.func()
"mocked stuff"

There is a reference in the docs here: http://docs.python.org/dev/library/unittest.mock.html#where-to-patch

Simon Luijk
  • 553
  • 5
  • 6
12

Let's assume you're creating your mock inside module foobar:

import util, mock
util.get_content = mock.Mock(return_value="mocked stuff")

If you import mymodule and call util.get_content without first importing foobar, your mock will not be installed:

import util
def func()
    print util.get_content()
func()
"stuff"

Instead:

import util
import foobar   # substitutes the mock
def func():
    print util.get_content()
func()
"mocked stuff"

Note that foobar can be imported from anywhere (module A imports B which imports foobar) as long as foobar is evaluated before util.get_content is called.

samplebias
  • 37,113
  • 6
  • 107
  • 103
2

While it doesn't provide an answer to your question directly, another possible alternative is to transform your function to a static method using the @staticmethod.

So you could transform your module utils into a class using something like:

class util(object):
     @staticmethod
     def get_content():
         return "stuff"

Then mock patches it correctly.

zom-pro
  • 1,571
  • 2
  • 16
  • 32