模拟ftplib.FTP以进行单元测试Python代码

问题描述 投票:8回答:2

我不知道为什么我只是没有得到这个,但我想在Python中使用mock来测试我的函数是否在ftplib.FTP中正确调用函数。我把所有东西简化了下来,但仍然没有把它包裹起来。这是一个简单的例子:

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”函数,而是从单独的模块文件中调用它。

python unit-testing python-mock
2个回答
8
投票

当你做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
投票

我建议使用pytest和pytest-mock。

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')
© www.soinside.com 2019 - 2024. All rights reserved.