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
开发者_如何学Gofrom 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.
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.
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.
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
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.
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.
精彩评论