Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to generate a file upload (test) request with Django REST Framework's APIRequestFactory?

I have developed an API (Python 3.5, Django 1.10, DRF 3.4.2) that uploads a video file to my media path when I request it from my UI. That part is working fine. I try to write a test for this feature but cannot get it to run successfully.

#views.py

import os
from rest_framework import views, parsers, response
from django.conf import settings


class FileUploadView(views.APIView):
    parser_classes = (parsers.FileUploadParser,)
    def put(self, request, filename):
        file = request.data['file']
        handle_uploaded_file(file, filename)
        return response.Response(status=204)

def handle_uploaded_file(file, filename):
    dir_name = settings.MEDIA_ROOT + '/scene/' + filename + '/cam1'
    new_filename = 'orig.mp4'
    if not os.path.exists(dir_name):
        os.makedirs(dir_name)
    file_path = os.path.join(dir_name, new_filename)
    with open(file_path, 'wb+') as destination:
        for chunk in file.chunks():
            destination.write(chunk)

and

#test.py

import tempfile
import os
from django.test import TestCase
from django.conf import settings
from django.core.files import File
from django.core.files.uploadedfile import SimpleUploadedFile
from rest_framework.test import APIRequestFactory
from myapp.views import FileUploadView


class UploadVideoTestCase(TestCase):
    def setUp(self):
        settings.MEDIA_ROOT = tempfile.mkdtemp(suffix=None, prefix=None, dir=None)

    def test_video_uploaded(self):
        """Video uploaded"""
        filename = 'vid'
        file = File(open('media/testfiles/vid.mp4', 'rb'))
        uploaded_file = SimpleUploadedFile(filename, file.read(), 'video')
        factory = APIRequestFactory()
        request = factory.put('file_upload/'+filename,
            {'file': uploaded_file}, format='multipart')
        view = FileUploadView.as_view()
        response = view(request, filename)
        print(response)

        dir_name = settings.MEDIA_ROOT + '/scene/' + filename + '/cam1'
        new_filename = 'orig.mp4'
        file_path = os.path.join(dir_name, new_filename)
        self.assertTrue(os.path.exists(file_path))

In this test, I need to use an existing video file ('media/testfiles/vid.mp4') and upload it since I need to test some processings on the video data after: that's why I reset the MEDIA_ROOT using mkdtemp.

The test fails since the file is not uploaded. In the def put of my views.py, when I print request I get <rest_framework.request.Request object at 0x10f25f048> and when I print request.data I get nothing. But if I remove the FileUploadParser in my view and use request = factory.put('file_upload/' + filename, {'filename': filename}, format="multipart") in my test, I get <QueryDict: {'filename': ['vid']}> when I print request.data.

So my conclusion is that the request I generate with APIRequestFactory is incorrect. The FileUploadParseris not able to retrieve the raw file from it.

Hence my question: How to generate a file upload (test) request with Django REST Framework's APIRequestFactory?

Several people have asked questions close to this one on SO but I had no success with the proposed answers.

Any help on that matter will be much appreciated!

like image 740
vwrobel Avatar asked Nov 06 '16 20:11

vwrobel


People also ask

What is APIRequestFactory?

APIRequestFactory : This is similar to Django's RequestFactory . It allows you to create requests with any http method, which you can then pass on to any view method and compare responses. APIClient : similar to Django's Client . You can GET or POST a URL, and test responses.

How do I run a test case in Django?

Open /catalog/tests/test_models.py.TestCase , as shown: from django. test import TestCase # Create your tests here. Often you will add a test class for each model/view/form you want to test, with individual methods for testing specific functionality.

What is Restapi in Django?

Django REST framework (DRF) is a powerful and flexible toolkit for building Web APIs. Its main benefit is that it makes serialization much easier. Django REST framework is based on Django's class-based views, so it's an excellent option if you're familiar with Django.


1 Answers

It's alright now! Switching from APIRequestFactory to APIClient, I managed to have my test running.

My new test.py:

import os
import tempfile
from django.conf import settings
from django.core.files import File
from django.core.files.uploadedfile import SimpleUploadedFile
from django.urls import reverse
from rest_framework.test import APITestCase, APIClient
from django.contrib.auth.models import User

class UploadVideoTestCase(APITestCase):
    def setUp(self):
        settings.MEDIA_ROOT = tempfile.mkdtemp()
        User.objects.create_user('michel')

    def test_video_uploaded(self):
        """Video uploaded"""
        filename = 'vid'
        file = File(open('media/testfiles/vid.mp4', 'rb'))
        uploaded_file = SimpleUploadedFile(filename, file.read(),
            content_type='multipart/form-data')
        client = APIClient()
        user = User.objects.get(username='michel')
        client.force_authenticate(user=user)
        url = reverse('file_upload:upload_view', kwargs={'filename': filename})
        client.put(url, {'file': uploaded_file}, format='multipart')
        dir_name = settings.MEDIA_ROOT + '/scene/' + filename + '/cam1'
        new_filename = 'orig.mp4'
        file_path = os.path.join(dir_name, new_filename)
        self.assertTrue(os.path.exists(file_path))
like image 69
vwrobel Avatar answered Oct 06 '22 06:10

vwrobel