Mocking Functions Using Python Mock

Question:

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.

Asked By: shreddd

||

Answers:

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.

Answered By: samplebias

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.

Answered By: shreddd

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

Answered By: Simon Luijk

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.

Answered By: zom-pro

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.

Answered By: KanAfghan
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.