1

I want to write a test for my code which uses an FTP library and does upload data via FTP.

I would like to avoid the need for a real FTP server in my test.

What is the most simple way to test my code?

There are several edge-cases which I would like to test.

For example, my code tries to create a directory which already exists.

I want to catch the exception and do appropriate error handling.

I know that I could use the mocking library. I used it before. But maybe there is a better solution for this use case?

Update Why I don't want to do mocking: I know that I could use mocking to solve this. I could mock the library I use (I use ftputil from Stefan Schwarzer) and test my code this way. But what happens if I change my code and use a different FTP library in the future? Then I would need to re-write my testing code, too. I am lazy. I want to be able to rewrite the real code I am testing without touching the test code. But maybe I am still missing a cool way to use mocking.

Solved with https://github.com/tbz-pariv/ftpservercontext

guettli
  • 25,042
  • 81
  • 346
  • 663
  • Firstly, you mean Fake, not Mock. Secondly you don't Fake the FTP, that is integration testing, abstract away the FTP library then either fake or mock the FTP interface. – Aron Sep 18 '18 at 10:56
  • I am using rebex sftp server portable to create a server locally on my machine. – Ninad Gaikwad Sep 18 '18 at 10:59
  • @guettli, What is the motive behind this test, mostly we are in a world where we use , test the codes /tech footprints for some or other kind of live demonstration. however, as other fellows already mentioned you can better look for mock libs if you don't want real time testing. – Karn Kumar Sep 26 '18 at 06:26
  • @pygo Thank you very much for your interest in this question. I updated my question. See "Why I don't want to do mocking". – guettli Sep 26 '18 at 07:56

2 Answers2

3

Firstly to hey this or of the way. You aren't asking about Mocking, your question is about Faking.

  • Fake, an implementation of an interface, which expresses correct behaviour, but cannot be used in production.

  • Mock, an implementation of an interface that responds to interactions based on a scripted (script as in movie script, not uncompiled code) response.

  • Stub, an implementation of an interface lacking any real implementation. Usually used in mcguffin style tests.

Notice that in every case the word "interface" is used.

Your question asks how to Fake a TCP port such that the behaviour is a FTP server, with STATE of a rw filesystem underneath.

This is hard.

It is much easier to MOCK an internal interface that throws when you call the mkdir function.

If you must FAKE a FTP server. I suggest creating a docker container with the server in the state you want and use docker to handle the repeatability and lifecycle of the FTP server.

Aron
  • 15,464
  • 3
  • 31
  • 64
2

ContextManager:

class FTPServerContext(object):

    banner = 'FTPServerContext ready'

    def __init__(self, directory_to_serve):
        self.directory_to_serve = directory_to_serve

    def __enter__(self):
        cmd = ['serve_directory_via_ftp']
        self.pipe = subprocess.Popen(cmd, cwd=self.directory_to_serve)
        time.sleep(2) # TODO check banner via https://stackoverflow.com/a/4896288/633961

    def __exit__(self, *args):
        self.pipe.kill()

console_script:

def serve_directory_via_ftp():
    # https://pyftpdlib.readthedocs.io/en/latest/tutorial.html
    authorizer = DummyAuthorizer()
    authorizer.add_user('testuser-ftp', 'testuser-ftp-pwd', '.', perm='elradfmwMT')
    handler = FTPHandler
    handler.authorizer = authorizer
    handler.banner = testutils.FTPServerContext.banner
    address = ('localhost', 2121)
    server = FTPServer(address, handler)
    server.serve_forever()

Usage in test:

def test_execute_job_and_create_log(self):
    temp_dir = tempfile.mkdtemp()
    with testutils.FTPServerContext(temp_dir) as ftp_context:
        execute_job_and_create_log(...)

Code is in the public domain under any license you want. It would great if you make this a pip installable package at pypi.org.

guettli
  • 25,042
  • 81
  • 346
  • 663
  • Just for the records, the author of pyftpdlib has already a ThreadedFtpServer which could be used for testing. I hope that API and docs are coming soon: https://github.com/giampaolo/pyftpdlib/issues/480#issuecomment-433375690 – guettli Oct 29 '18 at 09:12
  • This code is available via pypi now: https://github.com/tbz-pariv/ftpservercontext – guettli Dec 10 '18 at 11:38