2011-04-30 48 views
2

我的项目使用Python的urllib2.urlopen对外部API进行了各种调用。我正在使用NoseTests进行单元测试,MiniMock模拟urllib2.urlopen的呼叫。如何向Python中的模拟函数提供条件参数?

嘲讽代码:

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文件。但是,导入该模拟类会覆盖上面显示的类,因为每次都会使用损坏的XML来打破所有测试。

我认为这是因为异常测试模块被加载到其他模块之后,因此它的导入被称为last,并且模拟的函数返回破损的XML覆盖原始的模拟函数。

我希望能够告诉模拟代码在运行test_bar_exception时使用破损的XML文件,以便引发异常。我会如何去做这件事?

+0

为Python一个伟大的模拟系统,请查看[Fudge](http://farmdev.com/projects/fudge/)。 – 2011-05-01 00:19:52

回答

3

在我看来,你需要设置和拆卸你需要使用它的每个测试的模拟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: ... finally:进行换行,对于每个测试来说,这都是很多繁文tape节。

你可能想看看Michael Foord的mock库。

PYCON介绍:http://blip.tv/file/4881513

看看patch,让你的装饰器和对更换的urlopen和恢复您的通话上下文管理器选项。它非常漂亮!

0

假设你输入的要求是“一个URL”和输出是“aresponse”,并输入“节”输出“bresponse”,所以使用

@mock.patch('requests.get', mock.Mock(side_effect = lambda k:{'aurl': 'a response', 'burl' : 'b response'}.get(k, 'unhandled request %s'%k))) 
相关问题