Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/334.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python猴子补丁私有函数_Python_Monkeypatching - Fatal编程技术网

Python猴子补丁私有函数

Python猴子补丁私有函数,python,monkeypatching,Python,Monkeypatching,我有一个模块,它有一个函数(称之为a()),调用同一模块中定义的另一个函数(称之为\uu b())\uuu b()是一个函数,它通过urllib2与网站对话并获取一些数据。现在我正在尝试测试a(),但当然不希望我的单元测试在公共互联网上进行。因此,我在想,如果我可以使用一个返回固定数据的函数对\uu b()进行monkey-patch,那么我就可以为a()编写测试 更具体地说,我的模块看起来有点像: def a(): return __b("someval") def __b(args

我有一个模块,它有一个函数(称之为
a()
),调用同一模块中定义的另一个函数(称之为
\uu b()
\uuu b()
是一个函数,它通过
urllib2
与网站对话并获取一些数据。现在我正在尝试测试
a()
,但当然不希望我的单元测试在公共互联网上进行。因此,我在想,如果我可以使用一个返回固定数据的函数对
\uu b()
进行monkey-patch,那么我就可以为
a()
编写测试

更具体地说,我的模块看起来有点像:

def a():
    return __b("someval")

def __b(args):
    return something_complex_with_args
所以现在我想测试
a()
,但我需要用猴子修补
\uu b
。问题是A)关于monkey patch的绝大多数信息适用于类的方法,而不是模块中的函数,B)我想要monkey patch的函数是私有的。我愿意将
\u b
更改为非私有,如果这能使流程更可行,但我宁愿不这样做

建议

编辑:目前的测试类如下所示:

from unittest import TestCase

import mymodule

def newfn(args):
    return {"a" : "b"}

mymodule._b = newfn

class TestMyModule(TestCase):
    def test_basic(self):
        print(mymodule.a('somearg'))
from mock import patch

    class TestMyModule(TestCase):
        def test_basic(self):
            with patch('mymodule._b') as mock:
                mock.return_value={"a" : "b"} # put here what you want the mock function to return. You can make multiple tests varying these values.
                #keep the indentation. Determines the scope for the patch.
                print(mymodule.a('somearg'))

当我运行这个程序时,如果猴子补丁根本没有完成,我会看到输出,而不是看到
{'a':'b'}
被打印出来。

如果您的模块名为'foo',那么下面的程序应该可以工作

import foo

def patched_version():
    return 'Hello'

foo.__b = patched_version

print (foo.a())
foo.py在哪里

def a():
    return __b()

def __b():
    return 'Goodbye'

我也面临同样的问题,但最终找到了解决办法。问题是在unittest.TestCase类中使用monkey补丁时。以下是一个很好的解决方案:

如果您使用的是Python2,则需要安装“mock”库(http://www.voidspace.org.uk/python/mock/)使用简易安装或其他方式。该库已经与Python 3捆绑在一起

下面是代码的样子:

from unittest import TestCase

import mymodule

def newfn(args):
    return {"a" : "b"}

mymodule._b = newfn

class TestMyModule(TestCase):
    def test_basic(self):
        print(mymodule.a('somearg'))
from mock import patch

    class TestMyModule(TestCase):
        def test_basic(self):
            with patch('mymodule._b') as mock:
                mock.return_value={"a" : "b"} # put here what you want the mock function to return. You can make multiple tests varying these values.
                #keep the indentation. Determines the scope for the patch.
                print(mymodule.a('somearg'))

虽然这种方法显然比创建一个模拟函数(我们可以模拟实际的子函数,_b())更不方便,因为它具有基于不同参数返回不同值的逻辑,但是我们不必要地增加了更多出错的机会。在这种方法中,我们只需硬编码我们希望模拟函数返回的内容,并测试我们开始测试的实际函数,即a()。

我似乎无法重现您的问题(我对您的示例进行了一些调整,因为它根本没有按原样运行)。你是不是打错了什么东西(比如
mymodule.\u b
而不是
mymodule.\u b

mymodule.py

def a(x):
    return __b("someval")

def __b(args):
    return "complex_thingy: {}".format(args)
from unittest import TestCase

import mymodule

def newfn(args):
    return {"a" : "b"}

mymodule.__b = newfn

class TestMyModule(TestCase):
    def test_basic(self):
        print(mymodule.a('somearg'))
C:\TEMP>python -m unittest mytest
{'a': 'b'}
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

C:\TEMP>
import mymodule

def newfn(args):
    return {"a" : "b"}

mymodule.__b = newfn

print(mymodule.a('somearg'))
mytest.py

def a(x):
    return __b("someval")

def __b(args):
    return "complex_thingy: {}".format(args)
from unittest import TestCase

import mymodule

def newfn(args):
    return {"a" : "b"}

mymodule.__b = newfn

class TestMyModule(TestCase):
    def test_basic(self):
        print(mymodule.a('somearg'))
C:\TEMP>python -m unittest mytest
{'a': 'b'}
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

C:\TEMP>
import mymodule

def newfn(args):
    return {"a" : "b"}

mymodule.__b = newfn

print(mymodule.a('somearg'))
输出

def a(x):
    return __b("someval")

def __b(args):
    return "complex_thingy: {}".format(args)
from unittest import TestCase

import mymodule

def newfn(args):
    return {"a" : "b"}

mymodule.__b = newfn

class TestMyModule(TestCase):
    def test_basic(self):
        print(mymodule.a('somearg'))
C:\TEMP>python -m unittest mytest
{'a': 'b'}
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

C:\TEMP>
import mymodule

def newfn(args):
    return {"a" : "b"}

mymodule.__b = newfn

print(mymodule.a('somearg'))
似乎工作正常。


或单元测试之外:

mytest2.py

def a(x):
    return __b("someval")

def __b(args):
    return "complex_thingy: {}".format(args)
from unittest import TestCase

import mymodule

def newfn(args):
    return {"a" : "b"}

mymodule.__b = newfn

class TestMyModule(TestCase):
    def test_basic(self):
        print(mymodule.a('somearg'))
C:\TEMP>python -m unittest mytest
{'a': 'b'}
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

C:\TEMP>
import mymodule

def newfn(args):
    return {"a" : "b"}

mymodule.__b = newfn

print(mymodule.a('somearg'))
输出

C:\TEMP>python mytest2.py
{'a': 'b'}

C:\TEMP>

一般来说,如果模块外的任何东西需要访问函数(包括测试),您可能不应该使用
\u name
格式。改用
\u name
格式表示它是一个内部函数,同时仍然可以让确实需要访问它的东西访问它。名称损坏(
\u x
)与private不同,除非您确切知道它是什么,否则您可能不应该使用它。Python没有数据隐藏。@Lattyware:我知道它和private不一样,但它已经非常接近了。为什么要用双下划线来命名它?@AdamParkin,因为双下划线提供了名称混乱-名称被更改(查看文档了解原因和具体情况)。在Python中,您不需要将内容设置为私有。你的问题清楚地说明了为什么这是个坏主意。Python不是java或C++,写它就好像它会导致坏代码。因此,我仍然不明白为什么它通常是坏的。也许把它移到聊天室,因为它与我的问题不太相关?不,原始版本的uu b()仍然被调用(不管它是
\uu b
还是
\u b
)。再仔细研究一下,你的答案是有效的,但是当我将打印更改为在测试方法中时(就像我的问题中一样),调用了
\uu b
的原始版本,这是因为unittest的执行方式与“普通”Python非常不同。尝试将
foo.\uu b=patched\u version
放入测试的
setUp
功能中。如果您使用的不是
unittest
,那么YMMV。我正在使用
unittest
,并将其移动到
设置中
并没有解决问题。没有,对我来说仍然不起作用。我得到:
AttributeError:没有属性“\b”
将“private”函数添加到init.py使错误消失,但随后修补无效(仍在调用原始版本)。