我正在尝试使用python模块(http://www.voidspace.org.uk/python/mock/index.html)模拟一个函数(返回一些外部内容)。
我在模拟导入模块的函数时遇到了一些麻烦。
例如,在util.py中我有
def get_content():
return "stuff"
我想模拟util.get_content,以便返回其他内容。
我正在尝试这个:
util.get_content=Mock(return_value="mocked stuff")
如果在另一个模块中调用get_content
,它实际上似乎永远不会返回模拟对象。我在如何使用Mock方面遗漏了一些东西吗?
请注意,如果我调用以下内容,则可以正常工作:
>>> util.get_content=Mock(return_value="mocked stuff")
>>> util.get_content()
"mocked stuff"
但是,如果从另一个模块内部调用get_content,它将调用原始函数而不是模拟版本:
>>> from mymodule import MyObj
>>> util.get_content=Mock(return_value="mocked stuff")
>>> m=MyObj()
>>> m.func()
"stuff"
mymodule.py的内容
from util import get_content
class MyObj:
def func():
get_content()
所以我想我的问题是 - 如何从我调用的模块中调用Mocked版本的函数?
from module import function
似乎可以归咎于此,因为它没有指向Mocked函数。
答案 0 :(得分:27)
我想我有一个解决方法,虽然我还不太清楚如何解决一般情况
在mymodule中,如果我替换
from util import get_content
class MyObj:
def func():
get_content()
与
import util
class MyObj:
def func():
util.get_content()
模拟似乎被调用了。看起来命名空间需要匹配(这是有道理的)。然而,奇怪的是我希望
import mymodule
mymodule.get_content = mock.Mock(return_value="mocked stuff")
在我使用from / import语法(现在将get_content拉入mymodule)的原始情况下执行操作。但这仍然是指未被嘲笑的get_content。
证明命名空间很重要 - 只需要在编写代码时牢记这一点。
答案 1 :(得分:23)
您必须修补正在使用的功能。在您的情况下,将在mymodule模块中。
import mymodule
>>> mymodule.get_content = Mock(return_value="mocked stuff")
>>> m = mymodule.MyObj()
>>> m.func()
"mocked stuff"
此处的文档中有一个参考:http://docs.python.org/dev/library/unittest.mock.html#where-to-patch
答案 2 :(得分:9)
假设您正在模块foobar
中创建模拟:
import util, mock
util.get_content = mock.Mock(return_value="mocked stuff")
如果您导入mymodule
并在未先导入util.get_content
的情况下致电foobar
,则系统不会安装您的模拟内容:
import util
def func()
print util.get_content()
func()
"stuff"
相反:
import util
import foobar # substitutes the mock
def func():
print util.get_content()
func()
"mocked stuff"
请注意,只要在调用foobar
之前评估foobar
,就可以从任何地方导入util.get_content
(模块A导入导入foobar的B)。
答案 3 :(得分:8)
一般情况是使用patch
中的mock
。请考虑以下事项:
<强> 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()
请注意get_content
是如何被模拟的,它不是util.get_content
,而是mymodule.get_content
,因为我们在mymodule
中使用它。
上面已经使用mock v2.0.0,nosetests v1.3.7和python v2.7.9进行了测试。
答案 4 :(得分:1)
虽然它没有直接为您的问题提供答案,但另一种可能的替代方法是使用@staticmethod将您的函数转换为静态方法。
因此,您可以使用以下内容将模块工具转换为类:
class util(object):
@staticmethod
def get_content():
return "stuff"
然后正确地模拟补丁。