Dans mon application Django, j'ai une vue qui permet de télécharger des fichiers. L'extrait de code est comme ceci
...
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)
Je souhaite tester un peu la vue. Je prévois de tester le chemin heureux ainsi que le chemin d'échec ... c'est-à-dire le cas où le request.FILES
n'a pas de clé 'fichier', le cas où request.FILES['file']
a None
..
Comment puis-je configurer les données de publication pour le chemin heureux? Quelqu'un peut-il me le dire?
Depuis Django docs sur Client.post
:
La soumission de fichiers est un cas particulier. Pour POST un fichier, vous avez seulement besoin de fournissez le nom du champ de fichier en tant que clé et un descripteur de fichier au fichier vous souhaitez télécharger en tant que valeur. Par exemple:
c = Client()
with open('wishlist.doc') as fp:
c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp})
J'avais l'habitude de faire la même chose with open('some_file.txt') as fp:
mais j'avais besoin d'images, de vidéos et d'autres fichiers réels dans le référentiel et je testais également une partie d'un composant de base de Django qui est bien testé. C'est donc ce que je fais actuellement:
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 ...
Dans Python 3.5+ vous devez utiliser un objet bytes
au lieu de str
. Remplacez "file_content"
par b"file_content"
Cela fonctionne très bien, SimpleUploadedFile
crée une InMemoryFile
qui se comporte comme un téléchargement normal et vous pouvez choisir le nom, le contenu et le type de contenu.
Je vous recommande de consulter Django RequestFactory . C'est le meilleur moyen de simuler les données fournies dans la demande.
Cela dit, j'ai trouvé plusieurs failles dans votre code.
Donc, je vous recommande de refactoriser votre vue pour utiliser une fonction comme:
def upload_file_to_location(request, location=None): # Can use the default configured
Et fais quelques moqueurs là-dessus. Vous pouvez utiliser Python Mock .
PS: Vous pouvez également utiliser Django Test Client Mais cela voudrait dire que vous ajouterez autre chose à tester, car ce client utilisera des sessions, des réponses intermédiaires, etc. Rien de semblable au test unitaire.
Je fais quelque chose comme ceci pour ma propre application liée à un événement, mais vous devriez avoir suffisamment de code pour passer à votre propre cas d'utilisation
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)
Dans Django 1.7, TestCase pose un problème qui peut être résolu en utilisant open (chemin du fichier, 'rb'), mais en utilisant le client de test, nous n'avons aucun contrôle sur celui-ci. Je pense qu'il est probablement préférable de s'assurer que file.read () renvoie toujours des octets.
source: https://code.djangoproject.com/ticket/23912 , par KevinEtienne
Sans l'option rb, une erreur TypeError est générée:
TypeError: sequence item 4: expected bytes, bytearray, or an object with the buffer interface, str found
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)
J'ai fait quelque chose comme ça:
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')
la fonction create_image créera une image de sorte que vous n'ayez pas besoin de donner un chemin d'image statique.
Remarque: vous pouvez mettre à jour le code selon votre code . Ce code pour Python 3.6.