7

我有一个带有函数(调用它a())的模块,它调用同一模块中定义的另一个函数(调用它__b())。 __b()是一个与网站对话urllib2并获取一些数据的功能。现在我正在尝试测试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'}打印出来。

4

3 回答 3

4

我似乎无法重现您的问题(我已经稍微调整了您的示例,因为它根本没有按原样运行)。你是不是打错了一些东西(比如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>
于 2013-06-19T18:05:17.810 回答
2

如果您的模块被命名为“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'
于 2012-12-26T21:52:13.963 回答
0

我面临同样的问题,但最终得到了解决方案。问题是在 unittest.TestCase 类中使用猴子补丁时。这是工作正常的解决方案:

如果您使用的是 Python 2,则需要使用 easy_install 或其他方式安装“模拟”库 (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()。

于 2013-01-22T10:12:56.427 回答