如何在Python中为模拟函数提供条件参数?
我的项目使用Python的如何在Python中为模拟函数提供条件参数?,python,unit-testing,mocking,nose,Python,Unit Testing,Mocking,Nose,我的项目使用Python的urllib2.urlopen对外部API进行各种调用。我正在使用NoTests进行单元测试,并使用MiniMock模拟调用urlib2.urlopen 模拟代码: from hashlib import md5 from os.path import dirname, join from urllib2 import Request, urlopen from minimock import mock, restore def urlopen_stub(url,
urllib2.urlopen
对外部API进行各种调用。我正在使用NoTests进行单元测试,并使用MiniMock模拟调用urlib2.urlopen
模拟代码:
from hashlib import md5
from os.path import dirname, join
from urllib2 import Request, urlopen
from minimock import mock, restore
def urlopen_stub(url, data=None, timeout=30):
"""
Mock urllib2.urlopen and return a local file handle or create file if
not existent and then return it.
"""
if isinstance(url, Request):
key = md5(url.get_full_url()).hexdigest()
else:
key = md5(url).hexdigest()
data_file = join(dirname(__file__), 'cache', '%s.xml' % key)
try:
f = open(data_file)
except IOError:
restore() # restore normal function
data = urlopen(url, data).read()
mock('urlopen', returns_func=urlopen_stub, tracker=None) # re-mock it.
with open(data_file, 'w') as f:
f.write(data)
f = open(data_file, 'r')
return f
mock('urlopen', returns_func=urlopen_stub, tracker=None)
我正在这样运行我的测试:
from os.path import isfile, join
from shutil import copytree, rmtree
from nose.tools import assert_raises, assert_true
import urlopenmock
class TestMain(object):
working = 'testing/working'
def setUp(self):
files = 'testing/files'
copytree(files, self.working)
def tearDown(self):
rmtree(self.working)
def test_foo(self):
func_a_calling_urlopen()
assert_true(isfile(join(self.working, 'file_b.txt')))
def test_bar(self):
func_b_calling_urlopen()
assert_true(isfile(join(self.working, 'file_b.txt')))
def test_bar_exception(self):
assert_raises(AnException, func_c_calling_urlopen)
最初,我在一个单独的模块中对异常进行了测试,该模块导入了一个不同的模拟文件,当调用urlopen
时,该文件返回了一个损坏的XML文件。但是,导入该mocking类会覆盖上面显示的类,因为每次都会使用被破坏的XML,所以会破坏所有测试
我假设这是因为异常测试模块是在其他模块之后加载的,因此它的导入是在最后调用的,并且返回损坏的XML的模拟函数重写了原始模拟函数
我希望能够告诉模拟代码在运行test_bar_异常时使用损坏的XML文件,以便引发异常。我该怎么做呢?在我看来,您需要在每个需要使用它的测试上设置并拆除您的模拟urlopen,并使用不同的模拟urlopen为处理该错误条件的测试返回一个损坏的xml文件。比如:
class TestMain(object):
# ...
def test_foo(self):
mock('urlopen', returns_func=urlopen_stub, tracker=None)
func_a_calling_urlopen()
assert_true(isfile(join(self.working, 'file_b.txt')))
restore()
# ...
def test_bar_exception(self):
mock('urlopen',
returns_func=urlopen_stub_which_returns_broken_xml_file,
tracker=None)
assert_raises(AnException, func_c_calling_urlopen)
restore()
但是,如果测试引发异常并且未到达restore()
调用,则上述操作存在问题。您可以使用try:。。。最后:
然而,每项测试都有很多繁文缛节
你可能想看看迈克尔·福德的图书馆
Pycon演示文稿:
看看哪一个为您提供了装饰器和上下文管理器选项来替换和恢复对urlopen的调用。非常漂亮 假设请求的输入是“url”,输出是“aresponse”,输入的“burl”输出是“Breponse”,所以使用
@mock.patch('requests.get', mock.Mock(side_effect = lambda k:{'aurl': 'a response', 'burl' : 'b response'}.get(k, 'unhandled request %s'%k)))
要获得一个很棒的Python模拟系统,请查看。