Aidos Kenessov
Aidos Kenessov

Reputation: 31

How to mock file presence in the file server to test FastAPI Static Files?

I am using FastAPI Static Files class to download files from the server. Everything works fine and I can easily download files using URL.

Now, I need to write a test case for file download and I do not want to upload a file just for the test purpose. I want to mock file presence, so I can just ensure that the response status is 200.

However, I am always getting 404, even if I mock file existence. Can you please help me with that issue?

Here is how StaticFiles is configured on my test environment:

app_v1.mount("/files", StaticFiles(directory="server/api/v1/files"), name="files")

Here is my test case with mock:

def test_file_download(files_client: TestClient):
    mock_file_path = "server/api/v1/files/mocked_file.png"

    with patch('os.path.exists', return_value=True) as mock_exists:
        # Perform the GET request to download the file
        response_d = files_client.get("/files/mocked_file.png")  # Adjusted path

        # Validate the response
        assert response_d.status_code == 200
        assert mock_exists.called_once_with(mock_file_path)

Please note that if I remove mocking and physically place a file into the folder "server/api/v1/files/mocked_file.png" response status is 200.

Also, if I comment assert response_d.status_code == 200, the test runs successfully which shows that mock_exists.called_once_with(mock_file_path) was called. However somehow Staticfiles still responds with 404.

Upvotes: 1

Views: 170

Answers (1)

Vlad Rusu
Vlad Rusu

Reputation: 1479

StaticFiles needs to also open the file, not only check if it exists, which is the only thing that has been mocked.

You would need to actually mock all the system calls that the StaticFiles calls itself, which is going to be quite complex, and doesn't really make sense to do so. A better approach would be to create static files for the scope of your test.

I would simply create the file with some content in it and delete it after the test is over. There is a standard Python module called tempfile, but that one writes temporary files in /tmp, thus you have limited control over the absolute path of the files. For this simple test case, I think it's easier to simply create & delete the files you want to test.

To make it even safer (e.g. if the test exits with an exception, it might skip the cleanup of the temp files), I'd create a small temporary file manager.

import os


class MyTemporaryFile:
    def __init__(self, path: str, content: bytes):
        self.path = path
        self.content = content

    def __enter__(self):
        self.file = open(self.path, 'wb')
        self.file.write(self.content)
        self.file.close()
        return self.path

    def __exit__(self):
        os.unlink(self.path)

and use it in your test:

def test_foo():
    with MyTemporaryFile('/path/to/file', b'some test content') as filename:
        # test your code

This will automatically create the file with the content you pass, and ensure the file is removed after the with statement is finished, even if there was an error inside.

Upvotes: 1

Related Questions