2017-03-08 89 views
0

一直在爲我的代碼做幾天的測試用例而掙扎。 我寫了一個代碼,用一串URL或一串URL的列表形式批量下載具有給定輸入的文件。我的代碼目前支持4種協議(http,https,ftp,sftp)用Mock編寫Python2.7 unittest(URLError:<urlopen error unknown url type:http>)

我一直在閱讀關於模擬的東西,並觀看了一些視頻。我仍然無法將我在互聯網上看到的樣本應用於我的實際代碼。 :(

下面是我實施批量下載和單元測試我寫這仍然是失敗的。

def batch_download(url): 
req = Request(url) 
try: 
    print '\nStart downloading...' 
    response = urlopen(req) 
except URLError as e: 
    # No network connection or Invalid URL or The specified server doesn't exist. 
    if hasattr(e, 'reason'): 
     print 'We failed to reach a server.' 
     print 'Reason: ', e.reason 
    # HTTP Response that is not 200. 
    elif hasattr(e, 'code'): 
     print 'The server couldn\'t fulfill the request.' 
     print 'Error code: ', e.code 
else: 
    # Retrieve a redirected URL, if there is one. 
    real_url = response.geturl() 
    print real_url 
    saved_to = get_local_path(real_url) 
    urlretrieve(real_url, saved_to) 

    # meta_data = response.info()['Content-Length'] 
    # file_on_disk = os.stat(saved_to).st_size 
    # print '\nContent-Length: ' + meta_data 
    # print 'File on Disk after Download: ' + str(file_on_disk) 
    remove_partially_downloaded(real_url, response, saved_to) 

    urlcleanup() 
return 



def remove_partially_downloaded(url, response, local_path_to_file): 
meta_data = response.info()['Content-Length'] 
file_on_disk = os.stat(local_path_to_file).st_size 
print '\nContent-Length: ' + meta_data 
print 'File on Disk after Download: ' + str(file_on_disk) 

partial_download = int(meta_data) - file_on_disk 

if partial_download > 0: 
    print '\nThe following partially downloaded file ' + get_filename_from_url(url) + ' will be removed' 
    os.remove(local_path_to_file) 
else: 
    return 

有格式的單元測試代碼中的問題,所以我重視我的單元測試類的形象,而不是在這裏 enter image description here

而且我得到的失敗消息是以下 enter image description here

任何幫助表示讚賞。建議使用補丁和馬gicMock非常感謝。我知道這兩者的基本概念,但仍然無法弄清楚將這些內容合併到我的代碼中的位置。我對此很新。

回答

1

好吧,我認爲有兩個阻滯劑在發揮。首先,你正在執行的不僅僅是你的代碼。依賴性,如urllib2沒有正確隔離。實際上,該錯誤是在該庫的代碼中引發的。其次,目前還不清楚你想要測試什麼。爲了解決這個問題,我建議使用AAA pattern來編寫你的測試。

讓我們從第二個問題開始。你想有3個部分供你測試:

  • 排列。在第一部分中,您將設置執行代碼所需的所有內容。這包括創建嘲笑並指導他們做出反應,但是您會喜歡。
  • 舉動。現在你打電話給你想要測試的代碼單元,在這種情況下是batch_download。
  • 斷言。驗證您的代碼輸出等於您的預期結果,或驗證您的模擬是否按預期方式調用。理想情況下,您應該在本節僅包含一個斷言,因此每次測試只測試一件事情。

在測試中,您在設置mock之前調用batch_download。然後,你設置一些模擬(儘管不正確,見下文),然後調用你的代碼的另一個函數。沒有斷言。

也許很難爲您的方法提出一個簡單的AAA,單斷言測試。那是因爲你的功能在做很多事情。嘗試重構幾個功能,每個功能都做一個單一的,可直接測試的事情。 (我強烈建議你查閱Robert Martin的書「Clean Code」,它提供了幾個提示和指導方針來提高你的代碼質量。)

至於模擬。你不應該爲此使用urllib2 opener。首先,因爲那樣你會和你的代碼一起測試這個庫。測試成爲一種集成測試,並且不是很好,因爲它引入了原始方法中未找到的新邏輯(額外的失敗點)。其次,它更容易使用模擬庫。

一個簡單的方法來嘲笑這種依賴將使用補丁。作爲一個例子,讓我們來測試,如果的urlopen不會引發錯誤(返回響應),並使用getURL被呼籲的響應,那麼urlretrive將與預期的url和save_to路徑被稱爲:

import mock 
... 
@mock.patch('test_obj.urllib2') 
def test_batch_download(self, urllib2_mock): 
    # Arrange: 
    response_mock = mock.MagicMock() 
    real_url = 'http://sample.com' 
    response_mock.geturl.return_value = real_url 
    urllib2_mock.urlopen.return_value = response_mock 

    # Act: 
    test_obj.batch_download('http://originalurl.com') 

    # Assert: 
    expected_save_to_path = '/var/dl/sample.com' 
    urllib2_mock.urlretrive.assert_called_once_with(real_url, expected_save_to_path) 

希望這有助於。祝你好運!

+0

非常感謝Guido!當我開始爲這個函數編寫測試時,我一直在嘲笑Request和Response對象。在我的測試中使用urlopener,因爲我發現堆棧溢出的另一個帖子表明這是嘲笑響應的方法。不管怎樣,再次感謝! :) – mopkaloppt

相關問題