0

I have a Python code to read some files from FPT using FTPS and TLS version 1.2, this is the function, the credentials are read from AWS secret manager:

def ftp_connection(host, username, password):

    try:
        ftp_connection = ftplib.FTP_TLS(host, username, password)

        # set TLS version 1.2
        ftp_connection.ssl_version = ssl.PROTOCOL_TLSv1_2

        # switching to secure data connection
        ftp_connection.prot_p()

    except ftplib.all_errors as err:
        print(err)

    return ftp_connection

I wonder how I can write unit tests for this function, I'd like to test: 1.verify TLS v1.2 connection 2. verify it's using the secure data connection

I'm new to unit tests, anything else that can be added to the unit tests? Many thanks.

This is what I have tried by following the first answer in this page:

@patch('ftplib.FTP_TLS', autospec=True)
    def test_open_ftp_connection(self, mock_ftp_constructor):
        mock_ftp = mock_ftp_constructor.return_value
        read_news_ftp_read.open_ftp_connection('test_host', 'test_username', 'test_password')

        mock_ftp_constructor.assert_called_with('test_host', 'test_username', 'test_password')
        self.assertTrue(mock_ftp.login.called)

This gave me error:

  File "C:\User\AppData\Local\Continuum\anaconda3\envs\Env-python3.7\lib\unittest\case.py", line 692, in assertTrue
    raise self.failureException(msg)
AssertionError: False is not true


Assertion failed
wawawa
  • 2,835
  • 6
  • 44
  • 105
  • 1
    Doing anything external disqualifies the test to be a unit test. What you are trying to do is more an integration or end to end test. – Klaus D. Mar 01 '21 at 17:23
  • Hi @KlausD. I'm just trying to figure out what the best way to write unit test for this... – wawawa Mar 02 '21 at 10:40
  • For it to be a unit test you have to mock the FTP connection. In a unit test you don't test the `ftplib`, you also don't test the FTP connection. You are just testing if you are using the `ftplib` as it should be used. Use [`unittest.mock`](https://docs.python.org/3/library/unittest.mock.html) to replace `ftplib.FTP_TLS` with a `Mock` and test if the mock is used as planned. – Klaus D. Mar 02 '21 at 11:44
  • @KlausD. Hi I have tried `mock` but got an error, I've updated my question, could you take a look for me please? Many thanks. – wawawa Mar 02 '21 at 15:26
  • The mock stored the arguments to the constructor as attributes: e.g. `mock_ftp_constructor.host`. – Klaus D. Mar 02 '21 at 16:28

2 Answers2

0

Pay attention: Your ftp_connection() function doesn't call login() on ftp connection.

I don't see any login call in your production code. So the assert

self.assertTrue(mock_ftp.login.called)

will faill: so, if you just forgot to call login in your production code ... WOW the test worked like a charme and expose a production bug. Otherwise simply remove this assert.

Michele d'Amico
  • 22,111
  • 8
  • 69
  • 76
0

I think you're asking a less than ideal question:

how I can write unit tests for this function, I'd like to test: 1.verify TLS v1.2 connection 2. verify it's using the secure data connection

Both of those things are outside of your control - the FTP server could be down, or changed, or its certificates could expire. That means your unit test would pass one time and fail the next with no changes, and that makes it a poor unit test because it's not testing a "unit" of your code.

A alternative is to create a FTP server for the duration of the test e.g. https://pytest-localftpserver.readthedocs.io You can then add dummy files to match what you expect to be on the server, run your code, then check that it output what you were expecting. This isn't a perfect solution, but it is a pragmatic one that should give you confidence in your code, but not take an immense amount of time and energy to setup and use.

Another alternative is to do an end-to-end test, where you contact an external server. It doesn't tell you if a specific version of your code works, e.g. to find the change that broke stuff, but it will tell you if it was working when the end-to-end test was run.

afaulconbridge
  • 1,107
  • 9
  • 21