簡體   English   中英

在單元測試中模擬FTP

[英]Mocking FTP in unit test

假設我要測試此復雜功能:

def func(hostname, username, password):
    ftp = FTP(hostname, username, password)
    ftp.retrbinary('RETR README', open('README', 'wb').write)

測試之一是:

@patch('FTP')
def test_func_happy_path():
    mock_ftp = Mock()
    mock_ftp.retrbinary = Mock()
    MockFTP.return_value = mock_ftp()
    func('localhost', 'fred', 's3Kr3t')
    assert mock_ftp.retrbinary.called

但是,這將創建一個本地文件README,我顯然不希望這樣做。

有沒有一種方法可以模擬/修補程序open以便不創建任何文件?

顯然,作為一種變通方法,我可以確保將文件寫入一個臨時目錄 ,該目錄可以作為參數傳遞給func或在func創建並返回。

請注意,使用裝飾器@patch('__builtin__.open') ,會引起以下期望:

self = <Mock name=u'open()' spec='FTP' id='51439824'>, name = 'write'
    def __getattr__(self, name):
        if name in ('_mock_methods', '_mock_unsafe'):
            raise AttributeError(name)
        elif self._mock_methods is not None:
            if name not in self._mock_methods or name in _all_magics:
>               raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute 'write'

我將回調傳遞給ftp.retrbinary而不是函數調用。

因此,考慮到您不關心打開時會發生什么,可以直接對其進行模擬,使其停止編寫。 為此,您可以遵循模擬FTP采用的類似方法。 因此,考慮到這一點,您可以像這樣設置測試代碼:

import unittest
from mock import patch, Mock
from my_code import func

class SirTestsAlot(unittest.TestCase):

    @patch('my_code.open')
    @patch('my_code.FTP')
    def test_func_happy_path(self, MockFTP, m_open):
        MockFTP.return_value = Mock()
        mock_ftp_obj = MockFTP()

        m_open.return_value = Mock()

        func('localhost', 'fred', 's3Kr3t')

        assert mock_ftp_obj.retrbinary.called
        assert m_open.called
        # To leverage off of the other solution proposed, you can also
        # check assert called with here too
        m_open.assert_called_once_with('README', 'wb')


if __name__ == '__main__':
    unittest.main()

如您所見,我們在這里所做的是關於測試位置的嘲笑。 因此,考慮到這一點,我們正在針對my_codeopenFTP

現在在my_code ,什么都沒有改變:

from ftplib import FTP


def func(hostname, username, password):
    ftp = FTP(hostname, username, password)

    ftp.retrbinary('RETR README', open('README', 'wb').write)

運行此測試套件將成功返回。

另一種方法涉及使用mock_open

from unittest.mock import patch, mock_open
import ftplib


def func(hostname, username, password):
    ftp = ftplib.FTP(hostname, username, password)
    ftp.retrbinary('RETR README', open('README', 'wb').write)


@patch('ftplib.FTP')
def test_func_happy_path(MockFTP):
    mock_ftp = MockFTP.return_value  # returns another `MagicMock`
    with patch('__main__.open', mock_open(), create=True) as m:
        func('localhost', 'fred', 's3Kr3t')

    assert mock_ftp.retrbinary.called
    m.assert_called_once_with('README', 'wb')


test_func_happy_path()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM