[Python] How can I manually create an UploadFile in FastAPI?

Discussão em 'Python' iniciado por Stack, Março 18, 2025.

  1. Stack

    Stack Membro Participativo

    Background


    I am trying to write tests for my FastAPI application. I have an object, ImageRecord, which uploads an image to S3. Here's the method signature:

    async def upload_image(file: UploadFile, s3_client=None) -> 'ImageRecord':


    This method is called by the API logic (i.e. the actual @app.post function) to upload an object. I want to test this method by uploading an image file to moto - so a mocked S3 bucket. This method, in ImageRecord, expects an UploadFile because it is called from the function - so I can't just pass in the bytes. Additionally, I call file.content_type in my code to get the content type.

    The Issue


    I can't create an instance of my actual API and call it because, in order to inject the mocked S3 object, I have to call the Python method from ImageRecord directly - not via a HTTP request. However, this means that I need to create the UploadFile object manually. Here is how I am creating it:

    contents = open("resc/valid_image.jpg", "rb").read()
    upload_file = UploadFile(filename="test_image.jpg", file=io.BytesIO(contents))


    However, when I upload it to ImageRecord, I get the error:

    Invalid type for parameter ContentType, value: None, type: <class 'NoneType'>, valid types: <class 'str'>


    Because upload_file.content_type is None. In real API code, when FastAPI creates the file, the source code says:


    The content type of the request, from the headers
    ​

    But my manually created file has no headers, and does not accept a content_type argument in init.

    TL;DR


    A manually created FastAPI.UploadFile has no content_type, because that must be inferred from the headers. Is there any way to create an UploadFile instance with a content_type for tests? Or do I need to refactor my code so that it infers the content type from the contents, and skips UploadFile.content_type altogether?

    Continue reading...

Compartilhe esta Página