使用Python Mock模拟功能


70

我正在尝试Mock使用python模拟模块的函数(返回一些外部内容)。

我在导入模块的模拟函数时遇到了一些麻烦。

例如,在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()

所以我想我的问题是-如何从我调用的模块内部调用函数的模拟版本?

看来,这from module import function可能归咎于此,因为它没有指向Mocked函数。


我遵循了您的确切描述(在python 2.5中,在Linux上具有模拟0.7.0,在Linux上),并且工作正常。您还有更多细节可以提供吗?
内特

嗯-从顶层范围调用函数时,该函数的行为似乎与预期的一样。但是,当它从另一个模块或函数内部被调用时(即在调用堆栈的更下方),它不会表现出模拟行为。我在澄清示例以说明这一点。
2011年

好的,我尝试了您的新描述-即使从,我仍然可以得到正确的答案mymodule.func()。对我来说唯一的区别是我mymodule.func() returnutil.get_content(),而不仅仅是调用它。我觉得您的描述中肯定仍然缺少一些信息。您是否实际尝试过上面的确切描述?您的实际代码是什么?
内特

抱歉-我避免粘贴大量代码。您是正确的-我的简化示例并没有完全失败,但是我认为我已经部分找到了解决方案。将对此进行更新。
shreddd 2011年

奇怪-在我更复杂的Django测试用例中,事情无法正常进行,但是当我尝试将其简化时,Mock对象似乎像预期的那样通过了。我猜在导入中​​有一些区别,即创建略有不同的名称空间。
shreddd 2011年

Answers:


35

我认为我有一个解决方法,尽管在如何解决一般情况上还不太清楚

在中mymodule,如果我替换

from util import get_content

class MyObj:    
    def func():
        get_content()

import util

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

Mock似乎被调用。看起来名称空间需要匹配(这很有意义)。但是,奇怪的是我希望

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

在原来使用from / import语法(现在可get_content插入mymodule)的原始情况下实现技巧。但这仍然是指未经嘲弄的get_content

事实证明,名称空间很重要-编写代码时只需记住这一点。


1
我真的很喜欢一般情况下的答案。有时from util import get_context语法可以与模拟一起使用,有时却不能。有人解释吗?
johnboiles 2014年

1
非常感谢您的回答。我很困惑,为什么我的模拟只能在某些文件中工作而不能在其他文件中工作。
guettli 2014年

3
@johnboiles回答“为什么有时起作用,有时却不起作用”:导入顺序在这里很重要。如果模拟在导入之前被执行,则导入首先执行,那么它将不起作用。
guettli


21

一般情况是使用patchfrom 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

以上已通过模拟v2.0.0,nosetests v1.3.7和python v2.7.9进行了测试。


10

假设您要在模块内部创建模拟foobar

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

如果您在未先导入的情况下进行导入mymodule和调用,则不会安装模拟程序:util.get_contentfoobar

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可以从只要任何地方(模块A导入B,其进口取得foob​​ar)被导入foobar被评估前util.get_content被调用。


1

虽然它不能直接为您提供问题的答案,但另一个可能的选择是使用@static方法将函数转换为静态方法。

因此,您可以使用以下方法将模块实用程序转换为类:

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

然后模拟正确修补它。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.