Mocking ftplib.FTP for unit testing Python code

后端 未结 3 1174
不知归路
不知归路 2021-02-19 19:07

I don\'t know why I\'m just not getting this, but I want to use mock in Python to test that my functions are calling functions in ftplib.FTP correctly. I\'ve simplified everythi

3条回答
  •  不知归路
    2021-02-19 20:02

    Like Ibrohim's answer, I prefer pytest with mocker.

    I have went a bit further and have actually wrote a library which helps me to mock easily. Here is how to use it for your case.

    You start by having your code and a basic pytest function, with the addition of my helper library to generate mocks to modules and the matching asserts generation:

    import ftplib
    
    from mock_autogen.pytest_mocker import PytestMocker
    
    
    def download_file(hostname, file_path, file_name):
        ftp = ftplib.FTP(hostname)
        ftp.login()
        ftp.cwd(file_path)
    
    
    def test_download_file(mocker):
        import sys
        print(PytestMocker(mocked=sys.modules[__name__],
                           name=__name__).mock_modules().prepare_asserts_calls().generate())
        download_file('ftp.server.local', 'pub/files', 'wanted_file.txt')
    

    When you run the test for the first time, it would fail due to unknown DNS, but the print statement which wraps my library would give us this valuable input:

    ...
    mock_ftplib = mocker.MagicMock(name='ftplib')
    mocker.patch('test_29817963.ftplib', new=mock_ftplib)
    ...
    import mock_autogen
    ...
    print(mock_autogen.generator.generate_asserts(mock_ftplib, name='mock_ftplib'))
    

    I'm placing this in the test and would run it again:

    def test_download_file(mocker):
        mock_ftplib = mocker.MagicMock(name='ftplib')
        mocker.patch('test_29817963.ftplib', new=mock_ftplib)
    
        download_file('ftp.server.local', 'pub/files', 'wanted_file.txt')
    
        import mock_autogen
        print(mock_autogen.generator.generate_asserts(mock_ftplib, name='mock_ftplib'))
    

    This time the test succeeds and I only need to collect the result of the second print to get the proper asserts:

    def test_download_file(mocker):
        mock_ftplib = mocker.MagicMock(name='ftplib')
        mocker.patch(__name__ + '.ftplib', new=mock_ftplib)
    
        download_file('ftp.server.local', 'pub/files', 'wanted_file.txt')
    
        mock_ftplib.FTP.assert_called_once_with('ftp.server.local')
        mock_ftplib.FTP.return_value.login.assert_called_once_with()
        mock_ftplib.FTP.return_value.cwd.assert_called_once_with('pub/files')
    

    If you would like to keep using unittest while using my library, I'm accepting pull requests.

提交回复
热议问题