2011-04-30 68 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))) 
相關問題