使用python mock模拟函数

时间:2011-03-12 23:43:13

标签: python unit-testing mocking

我正在尝试使用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函数。

5 个答案:

答案 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"

然后正确地模拟补丁。