繁体   English   中英

Python Monkey补丁私有功能

[英]Python monkey patch private function

我有一个带有函数的模块(称为a() ),该模块调用了在同一模块中定义的另一个函数(称为__b() )。 __b()是一个通过urllib2与网站__b()并获取一些数据的函数。 现在,我正在尝试测试a() ,但是当然不希望我的单元测试与公共互联网对话。 因此,我在考虑是否可以用返回固定数据的函数来修补__b() ,然后可以为a()编写测试。

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

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

def __b(args):
    return something_complex_with_args

所以现在我想测试a() ,但是我需要猴子修补__b 问题在于,A)有关猴子修补的绝大多数信息都适用于类的方法,而不适用于模块中的函数,以及B)我想进行猴子修补的函数是私有的。 我愿意将__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'))

当我运行此命令时,如果根本没有完成猴子修补,则会看到输出,而不是看到{'a': 'b'}被打印出来。

我似乎无法重现您的问题(我稍微调整了您的示例,因为它根本没有按原样运行)。 您是否只是输错了某些内容(例如mymodule._b而不是mymodule.__b )?

mymodule.py

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

def __b(args):
    return "complex_thingy: {}".format(args)

mytest.py

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>

似乎工作正常。


或在单元测试之外:

mytest2.py

import mymodule

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

mymodule.__b = newfn

print(mymodule.a('somearg'))

输出

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

C:\TEMP>

如果您的模块被命名为“ 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类中使用猴子补丁时。 这是可行的解决方案:

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

代码如下所示:

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()。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM