How to unit test file upload in django

In my django application, I have a view that downloads a file. A kernel snippet is similar to this

... if (request.method == 'POST'): if request.FILES.has_key('file'): file = request.FILES['file'] with open(settings.destfolder+'/%s' % file.name, 'wb+') as dest: for chunk in file.chunks(): dest.write(chunk) 

I would like to unit test view.I plan to test the happy path as well as the fail..ie path, the case where request.FILES does not have a key "file", the case where request.FILES['file'] has None ..

How to set up mail data for a happy journey? Can anyone tell me?

+81
django unit-testing file-upload
Jun 23 2018-12-12T00:
source share
7 answers

From Django docs to Client.post :

Sending files is a special case. For a POST file you only need to specify the file name as the key and the file descriptor in the file you want to load as the value. For example:

 c = Client() with open('wishlist.doc') as fp: c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp}) 
+97
Jun 23 2018-12-12T00:
source share

I used the same with open('some_file.txt') as fp: but then I needed images, videos and other real files in the repo, and I also tested part of the main Django component, which is well tested, so in Currently, this is what I have:

 from django.core.files.uploadedfile import SimpleUploadedFile def test_upload_video(self): video = SimpleUploadedFile("file.mp4", "file_content", content_type="video/mp4") self.client.post(reverse('app:some_view'), {'video': video}) # some important assertions ... 

In Python 3.5+, you need to use the bytes object instead of str . Change "file_content" to b"file_content"

It works fine, SimpleUploadedFile creates an InMemoryFile that behaves like a normal download, and you can choose a name, content, and content type.

+83
Dec 07 '14 at 17:07
source share

I recommend you take a look at the Django RequestFactory . This is the best way to mock the data specified in the query.

Said I found a few flaws in your code.

  • “unit” testing means testing only one “unit” of functionality. So, if you want to test this view, you will test this view, and the file system, and therefore not quite unit testing. To make this point clearer. If you run this test and the view will work fine, but you do not have permission to save this file, your test will fail because of this.
  • Another important thing is the speed of the test . If you are doing something like TDD, the speed of your tests is really important. Access to any input / output is not a good idea .

So, I recommend that you reorganize your view to use a function such as:

 def upload_file_to_location(request, location=None): # Can use the default configured 

And laugh at that. You can use Python Mock .

PS: You can also use the Django Test Client, but this will mean that you add one more thing for testing, because this client uses Sessions, middleware, etc. Nothing like unit testing.

+6
Jun 23 '12 at 15:00
source share

I am doing something similar for my own event-related application, but you should have more than enough code to work with your own use case.

 import tempfile, csv, os class UploadPaperTest(TestCase): def generate_file(self): try: myfile = open('test.csv', 'wb') wr = csv.writer(myfile) wr.writerow(('Paper ID','Paper Title', 'Authors')) wr.writerow(('1','Title1', 'Author1')) wr.writerow(('2','Title2', 'Author2')) wr.writerow(('3','Title3', 'Author3')) finally: myfile.close() return myfile def setUp(self): self.user = create_fuser() self.profile = ProfileFactory(user=self.user) self.event = EventFactory() self.client = Client() self.module = ModuleFactory() self.event_module = EventModule.objects.get_or_create(event=self.event, module=self.module)[0] add_to_admin(self.event, self.user) def test_paper_upload(self): response = self.client.login(username=self.user.email, password='foz') self.assertTrue(response) myfile = self.generate_file() file_path = myfile.name f = open(file_path, "r") url = reverse('registration_upload_papers', args=[self.event.slug]) # post wrong data type post_data = {'uploaded_file': i} response = self.client.post(url, post_data) self.assertContains(response, 'File type is not supported.') post_data['uploaded_file'] = f response = self.client.post(url, post_data) import_file = SubmissionImportFile.objects.all()[0] self.assertEqual(SubmissionImportFile.objects.all().count(), 1) #self.assertEqual(import_file.uploaded_file.name, 'files/registration/{0}'.format(file_path)) os.remove(myfile.name) file_path = import_file.uploaded_file.path os.remove(file_path) 
+4
Jun 23 '12 at 16:50
source share

I did something like this:

 from django.core.files.uploadedfile import SimpleUploadedFile from django.test import TestCase from django.core.urlresolvers import reverse from django.core.files import File from django.utils.six import BytesIO from .forms import UploadImageForm from PIL import Image from io import StringIO def create_image(storage, filename, size=(100, 100), image_mode='RGB', image_format='PNG'): """ Generate a test image, returning the filename that it was saved as. If ''storage'' is ''None'', the BytesIO containing the image data will be passed instead. """ data = BytesIO() Image.new(image_mode, size).save(data, image_format) data.seek(0) if not storage: return data image_file = ContentFile(data.read()) return storage.save(filename, image_file) class UploadImageTests(TestCase): def setUp(self): super(UploadImageTests, self).setUp() def test_valid_form(self): ''' valid post data should redirect The expected behavior is to show the image ''' url = reverse('image') avatar = create_image(None, 'avatar.png') avatar_file = SimpleUploadedFile('front.png', avatar.getvalue()) data = {'image': avatar_file} response = self.client.post(url, data, follow=True) image_src = response.context.get('image_src') self.assertEquals(response.status_code, 200) self.assertTrue(image_src) self.assertTemplateUsed('content_upload/result_image.html') 

The create_image function will create an image, so you do not need to specify a static path to the image.

Note. You can update the code according to your code. This code is for Python 3.6.

+2
Feb 02 '18 at 8:46
source share

In Django 1.7, there is a problem with TestCase, which can be resolved with open (file path, "rb"), but when using the test client we do not control it. I think that it is best to ensure that file.read () always returns bytes.

source: https://code.djangoproject.com/ticket/23912 by KevinEtienne

Without the rb parameter, a TypeError occurs:

 TypeError: sequence item 4: expected bytes, bytearray, or an object with the buffer interface, str found 
+1
Mar 10 '15 at 22:58
source share
 from rest_framework.test import force_authenticate from rest_framework.test import APIRequestFactory factory = APIRequestFactory() user = User.objects.get(username='#####') view = <your_view_name>.as_view() with open('<file_name>.pdf', 'rb') as fp: request=factory.post('<url_path>',{'file_name':fp}) force_authenticate(request, user) response = view(request) 
0
Jul 11 '19 at 10:30
source share



All Articles