使用python mock模拟函数

shr*_*ddd 60 python unit-testing mocking

我正在尝试使用python模块模块(http://www.voidspace.org.uk/python/mock/index.html)模拟一个函数(返回一些外部内容).

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

例如,在util.py中我有

def get_content():
  return "stuff"
Run Code Online (Sandbox Code Playgroud)

我想模拟util.get_content,以便返回其他内容.

我在尝试这个:

util.get_content=Mock(return_value="mocked stuff")
Run Code Online (Sandbox Code Playgroud)

如果get_content在另一个模块中调用它,它实际上似乎永远不会返回模拟对象.我是否错过了如何使用模拟的东西?

请注意,如果我调用以下内容,则可以正常工作:

>>> util.get_content=Mock(return_value="mocked stuff")
>>> util.get_content()
"mocked stuff"
Run Code Online (Sandbox Code Playgroud)

但是,如果从另一个模块内部调用get_content,它将调用原始函数而不是模拟版本:

>>> from mymodule import MyObj
>>> util.get_content=Mock(return_value="mocked stuff")
>>> m=MyObj()
>>> m.func()
"stuff"
Run Code Online (Sandbox Code Playgroud)

mymodule.py的内容

from util import get_content

class MyObj:    
    def func():
        get_content()
Run Code Online (Sandbox Code Playgroud)

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

这似乎from module import function可能是责备,因为它没有指向Mocked函数.

shr*_*ddd 28

我想我有一个解决方法,虽然我还不太清楚如何解决一般情况

在mymodule中,如果我更换

from util import get_content

class MyObj:    
    def func():
        get_content()
Run Code Online (Sandbox Code Playgroud)

import util

class MyObj:    
    def func():
        util.get_content()
Run Code Online (Sandbox Code Playgroud)

Mock似乎被调用了.看起来命名空间需要匹配(这是有道理的).然而,奇怪的是我会期待

import mymodule
mymodule.get_content = mock.Mock(return_value="mocked stuff")
Run Code Online (Sandbox Code Playgroud)

在我使用from/import语法(现在将get_content拉入mymodule)的原始情况下做的诀窍.但这仍然指的是未被嘲笑的get_content.

结果是命名空间很重要 - 只需要在编写代码时牢记这一点.

  • @johnboiles回答"为什么它有时有效,有时不行":进口的顺序在这里很重要.如果你在导入之前执行了mock,那么如果首先执行导入,那么它就不起作用了. (3认同)
  • 我真的很喜欢一般情况的答案。有时“from util import get_context”语法适用于模拟,有时则不然。有人有解释吗? (2认同)

Sim*_*ijk 23

您必须修补使用它的功能.在你的情况下,将在mymodule模块中.

import mymodule
>>> mymodule.get_content = Mock(return_value="mocked stuff")
>>> m = mymodule.MyObj()
>>> m.func()
"mocked stuff"
Run Code Online (Sandbox Code Playgroud)

这里的文档中有一个参考:http://docs.python.org/dev/library/unittest.mock.html#where-to-patch


小智 12

一般情况下是使用patchmock.考虑以下:

utils.py

def get_content():
    return 'stuff'
Run Code Online (Sandbox Code Playgroud)

mymodule.py

from util import get_content


class MyClass(object):

    def func(self):
        return get_content()
Run Code Online (Sandbox Code Playgroud)

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()
Run Code Online (Sandbox Code Playgroud)

注意如何get_content被嘲笑,它不是util.get_content,而是mymodule.get_content因为我们正在使用它mymodule.

上面已经用mock v2.0.0测试,nosetests v1.3.7和python v2.7.9.


sam*_*ias 9

我们假设您正在创建模拟内部模块foobar:

import util, mock
util.get_content = mock.Mock(return_value="mocked stuff")
Run Code Online (Sandbox Code Playgroud)

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

import util
def func()
    print util.get_content()
func()
"stuff"
Run Code Online (Sandbox Code Playgroud)

代替:

import util
import foobar   # substitutes the mock
def func():
    print util.get_content()
func()
"mocked stuff"
Run Code Online (Sandbox Code Playgroud)

注意,foobar只要foobarutil.get_content调用之前进行评估,就可以从任何地方导入(模块A导入导入foobar的B).