如何使用Django REST Framework的APIRequestFactory生成文件上传(测试)请求?

11

我开发了一个API(Python 3.5,Django 1.10,DRF 3.4.2),当我从我的UI请求时,它会将视频文件上传到我的媒体路径。那部分工作正常。我尝试为此功能编写一个测试,但无法成功运行。

#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)

#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))
在这个测试中,我需要使用现有的视频文件('media/testfiles/vid.mp4')并上传它,因为我需要在视频数据上测试一些处理过程:这就是为什么我使用mkdtemp重置MEDIA_ROOT的原因。
测试失败,因为文件没有被上传。在我的views.pydef put中,当我打印request时,我得到<rest_framework.request.Request object at 0x10f25f048>,当我打印request.data时,我什么也没有得到。但是,如果我从我的视图中删除FileUploadParser并在我的测试中使用request = factory.put('file_upload/' + filename, {'filename': filename}, format="multipart"),当我打印request.data时,我得到<QueryDict: {'filename': ['vid']}>
因此,我的结论是,我用APIRequestFactory生成的请求不正确。FileUploadParser无法从中检索原始文件。
因此,我的问题是:如何使用Django REST Framework的APIRequestFactory生成文件上传(测试)请求? 许多人在SO上提出了类似于这个问题的问题,但我没有成功地使用所提出的答案。
对于这个问题的任何帮助都将不胜感激!
2个回答

14

没问题啦!从APIRequestFactory换成APIClient后,我的测试成功运行了。

这是我的新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))

3
以下是使用APIRequestFactory(和ModelViewSet)测试文件上传的请求。
from rest_framework.test import APIRequestFactory, APITestCase
from my_project.api.views import MyViewSet
from io import BytesIO

class MyTestCase(APITestCase):

    def setUp(self):
        fd = BytesIO(b'Test File content')   # in-memory file to upload
        fd.seek(0)                           # not needed here, but to remember after writing to fd
        reqfactory = APIRequestFactory()     # initialize in setUp if used by more tests
        view = MyViewSet({'post': 'create'}) # for ViewSet {action:method} needed, for View, not.
        request = factory.post('/api/new_file/',
            {
                "title": 'test file',
                "fits_file": self.fd,
            },
            format='multipart')              # multipart is default, but for clarification that not json
        response = view(request)
        response.render()
        self.assertEqual(response.status_code, 201)



请注意,与以下代码相似,没有授权的清晰度:'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.AllowAny']

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接