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.
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
.
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
alltogether?
I had the same issue and managed to resolve it by setting the headers and specifying the
content-type
in the headers object, before passing it to UploadFile:
headers = {
'content-type': mime_type
}
media_file = UploadFile(file=file_content, filename=file_name, headers=headers)
Now calling media_file.content_type
correctly returns the content type for the file.