2012-06-23 5 views
56

In meiner django app, ich habe einen Blick haben, die Datei upload.The Kern Schnipsel erreicht wie dieses ist, wie Unit-Test-Datei-Upload in django

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

Ich möchte Test Einheit die view.I Planung bin den glücklichen Weg zu testen sowie die path..ie scheitern, den Fall, dass die request.FILES keinen Schlüssel ‚Datei‘ hat, Fall, in dem request.FILES['file']None hat ..

wie kann ich die Post-Daten für den glücklichen Weg nach oben Kann mir das jemand sagen?

+0

Da Sie die Antwort mit Client-Klasse als richtig markiert haben, suchen Sie wahrscheinlich keinen Komponententest, sondern einen Funktionstest ... – Henning

Antwort

79

Von Django docs auf Client.post:

Submitting files is a special case. To POST a file, you need only provide the file field name as a key, and a file handle to the file you wish to upload as a value. For example:

c = Client() 
with open('wishlist.doc') as fp: 
    c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp}) 
+7

Link zu den relevanten Django doc: https://docs.djangoproject.com/ de/dev/topics/tests/übersicht/# django.test.client.Client.post – lsh

+2

tote Verbindung, siehe https://docs.djangoproject.com/en/1.7/topics/testing/tools/#django.test. Client.post –

+3

IMHO mit client ist es kein Unit Test mehr;) – Henning

5

Ich empfehle Ihnen RequestFactory einen Blick auf Django zu nehmen. IT ist der beste Weg, die in der Anfrage bereitgestellten Daten nachzuahmen.

Sagte, dass ich mehrere Fehler in Ihrem Code gefunden.

  • "unit" Prüfmittels nur eine "Einheit" von functionallity zu testen. Also, Wenn Sie diese Ansicht testen möchten, würden Sie die Ansicht testen, und die Datei System, ergo, nicht wirklich Komponententest. Um diesen Punkt klarer zu machen. Wenn Sie diesen Test ausführen, und die Ansicht funktioniert gut, aber Sie haben keine Berechtigungen, um diese Datei zu speichern, würde Ihr Test scheitern.
  • Andere wichtige Sache ist Testgeschwindigkeit. Wenn Sie etwas wie TDD tun, ist die Geschwindigkeit der Ausführung Ihrer Tests sehr wichtig. Zugriff auf alle E/A ist keine gute Idee.

So empfehle ich Ihnen zu refactor Ihrer Sicht eine Funktion wie zu verwenden:

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

Und dass einige spöttische zu tun. Sie können Python Mock verwenden.

PS: Sie könnten auch Django verwenden Test Client Aber das würde bedeuten, dass Sie eine andere Sache mehr zum Test hinzufügen, weil Client Sessions, Middleweres, etc. verwenden. Nichts ähnliches wie Unit Testing.

+1

Ich könnte falsch liegen, aber es scheint, als ob er zum Integrationstest meinte und nur den Begriff 'Komponententest' falsch verwendete. – jooks

+0

@santiagobasulto Ich bin ein Neuling in TDD und möchte meine Unit-Tests beschleunigen. Aber ich habe mehrere Ansichten, die sich mit Datei-Uploads beschäftigen, die während des Komponententests Dateien auf den Remote-Speicher (Amazon S3) hochladen. Das braucht Zeit. Könnten Sie bitte Ihre Antwort erweitern, um ausführlich zu zeigen, wie Sie während des Testens nicht auf E/A zugreifen können? –

+3

Hey @Dmitry. Mock ist der Weg dorthin. Wann immer Sie auf eine externe Ressource zugreifen müssen, sollten Sie sich darüber lustig machen. Angenommen, Sie haben eine Ansicht namens 'profile_picture', die intern eine' upload_profile_picture'-Funktion verwendet. Wenn Sie diese Ansicht testen möchten, machen Sie sich einfach über die interne Funktion lustig und stellen Sie sicher, dass sie bei Ihrem Test aufgerufen wird. Dies ist ein einfaches Beispiel: https://gist.github.com/santiagobasulto/6437356 – santiagobasulto

4

ich so etwas für meine eigene ereignisbezogene Anwendung, aber Sie sollten mehr als genug Code müssen Sie sich mit Ihrem eigenen Anwendungsfall erhalten

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

ich das gleiche with open('some_file.txt') as fp: tun verwendet, aber dann brauchte ich Bilder , Videos und andere echte Dateien im Repo und auch so aktuell ist, ich war ein Teil eines Django Kernkomponente zu testen, die, gut getestet wird das, was ich getan habe:

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+ y Sie müssen bytes Objekt anstelle von str verwenden.Ändern "file_content" zu b"file_content"

Es ist schon gut funktioniert, SimpleUploadedFile schafft eine InMemoryFile, die wie bei einer normalen Upload verhält und Sie können den Namen, den Inhalt und Inhaltstyp auswählen.

+0

In Ihrem Beispiel gibt mir die Formularvalidierung Folgendes: "Laden Sie ein gültiges Bild hoch. Die hochgeladene Datei war entweder kein Bild oder ein beschädigtes Bild." – antonagestam

+0

@antonagestam Übergeben Sie den richtigen Inhaltstyp? Prüft Ihr Formular den Inhalt der Datei? Wenn dies der Fall ist, muss "file_content" ein gültiger Image-Header sein, damit der Code ein gültiges Image sieht. –

+0

Was sind die entsprechenden Header für JPEG und PNG? – antonagestam

0

In Django 1.7 gibt es ein Problem mit dem Testfall, das mit open (Dateipfad, 'rb') gelöst werden kann, aber wenn wir den Testclient verwenden, haben wir keine Kontrolle darüber. Ich denke, es ist wahrscheinlich am besten zu gewährleisten, dass file.read() immer Bytes zurückgibt.

Quelle: https://code.djangoproject.com/ticket/23912, von KevinEtienne

Ohne rb Option wird ein Typeerror angehoben:

TypeError: sequence item 4: expected bytes, bytearray, or an object with the buffer interface, str found 
2

Ich habe so etwas wie das:

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

create_image Funktion Bild erstellen wird, so dass Sie muss den statischen Pfad des Bildes nicht angeben.

Hinweis: Sie können den Code gemäß Ihrem Code aktualisieren. Dieser Code für Python 3.6.