2015-04-23 161 views
6

我不知道爲什麼我只是沒有得到這個,但我想在Python中使用模擬來測試我的函數正確調用ftplib.FTP函數。我已經簡化了所有的工作,並且還沒有圍繞它如何工作。下面是一個簡單的例子:模擬ftplib.FTP單元測試Python代碼

import unittest 
import ftplib 
from unittest.mock import patch 

def download_file(hostname, file_path, file_name): 
    ftp = ftplib.FTP(hostname) 
    ftp.login() 
    ftp.cwd(file_path) 

class TestDownloader(unittest.TestCase): 

    @patch('ftplib.FTP') 
    def test_download_file(self, mock_ftp): 
     download_file('ftp.server.local', 'pub/files', 'wanted_file.txt') 

     mock_ftp.cwd.assert_called_with('pub/files') 

當我跑,我得到:

AssertionError: Expected call: cwd('pub/files') 
Not called 

我知道它必須是使用模擬對象,因爲這是一個假的服務器名稱,並在沒有補丁運行它會拋出一個「socket.gaierror」異常。

如何獲得功能運行的實際對象?長期目標是在同一個文件中沒有「download_file」函數,而是從一個單獨的模塊文件調用它。

回答

7

當你做patch(ftplib.FTP)你正在修補FTP構造函數。 dowload_file()用它來建立ftp對象,以便您ftp對象上調用login()cmd()mock_ftp.return_value而不是mock_ftp

你的測試代碼應遵循:

class TestDownloader(unittest.TestCase): 

    @patch('ftplib.FTP', autospec=True) 
    def test_download_file(self, mock_ftp_constructor): 
     mock_ftp = mock_ftp_constructor.return_value 
     download_file('ftp.server.local', 'pub/files', 'wanted_file.txt') 
     mock_ftp_constructor.assert_called_with('ftp.server.local') 
     self.assertTrue(mock_ftp.login.called) 
     mock_ftp.cwd.assert_called_with('pub/files') 

我添加了所有的檢查和autospec=True僅僅因爲是一個good practice

0

我建議使用pytestpytest-模擬

from pytest_mock import mocker 


def test_download_file(mocker): 
    ftp_constructor_mock = mocker.patch('ftplib.FTP') 
    ftp_mock = ftp_constructor_mock.return_value 

    download_file('ftp.server.local', 'pub/files', 'wanted_file.txt') 

    ftp_constructor_mock.assert_called_with('ftp.server.local') 
    assert ftp_mock.login.called 
    ftp_mock.cwd.assert_called_with('pub/files')