How to test file upload in Django rest framework using PUT?
Asked Answered
W

2

11

I would like to write a unit test for a view on a Django REST Framework application. The test should upload a file using PUT, in essence equivalent to

http -a malkarouri PUT http://localhost:8000/data-packages/upload/ka @tmp/hello.py

The code I have written so far is

factory = APIRequestFactory()
request = factory.put(                                                                                                                                                                '/data-packages/upload/ka',
    data,
    content_type='application/octet-stream',
    content_disposition="attachment; filename=data.dump")
force_authenticate(request, user)
view = PackageView.as_view()
response = view(request, "k.py")

which, obviously, does not upload a file. The specific error when running the test is 400:

{u'detail': u'Missing filename. Request should include a Content-Disposition header with a filename parameter.'}

Notably, I am using a request factory to test the view rather than a full client. That is what makes solutions such as the one in this question not work for me.

What is the correct way to set the content disposition header?

Wharfage answered 15/6, 2017 at 12:9 Comment(0)
S
10

Hi you need to use the SimpleUploadedFile wrapper for that :

from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.files import File

data = File(open('path/bond-data.dump', 'rb'))
upload_file = SimpleUploadedFile('data.dump', data.read(),content_type='multipart/form-data')
request = factory.put( '/data-packages/upload/ka',
   {'file':upload_file,other_params},
    content_type='application/octet-stream',
    content_disposition="attachment; filename=data.dump")

Ps : I am using APITestCase

Shawntashawwal answered 15/6, 2017 at 12:21 Comment(4)
I am afraid it didn't work for me. The PUT is designed to accept a file directly, not a form with a file parameter. Would that be the problem?Wharfage
it's not a problem, this is not a form wrapper, this is a file wrapper and add all the headers needed for being handled in django. At least i use it this way for testing the upload of documents on our website.Shawntashawwal
For me, I had to remove the argument content_type='application/octet-stream' and instead had to add format='multipart' factory.put in order to get it running.Mercurochrome
good answer, but it would be nice if the file path used os.join or something else to find the file, maybe from the Django static or uploads folders.Civism
M
8
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase

from rest_framework.test import APIClient


class MyTest(TestCase):
    client_class = APIClient

    def test_it(self):
        file = SimpleUploadedFile("file.txt", b"abc", content_type="text/plain")
        payload = {"file": file}
        response = self.client.post("/some/api/path/", payload, format="multipart")
        self.assertEqual(response.status_code, 201)

        # If you do more calls in this method with the same file then seek to zero
        file.seek(0)
Maynard answered 7/8, 2019 at 18:31 Comment(1)
This seems like a POST call rather than a PUT. No?Wharfage

© 2022 - 2024 — McMap. All rights reserved.