55

I have a form like:

#forms.py
from django import forms

class MyForm(forms.Form):
    title = forms.CharField()
    file = forms.FileField()


#tests.py
from django.test import TestCase
from forms import MyForm

class FormTestCase(TestCase)
    def test_form(self):
        upload_file = open('path/to/file', 'r')
        post_dict = {'title': 'Test Title'}
        file_dict = {} #??????
        form = MyForm(post_dict, file_dict)
        self.assertTrue(form.is_valid())

How do I construct the file_dict to pass upload_file to the form?

Jason Christa
  • 12,150
  • 14
  • 58
  • 85

5 Answers5

93

So far I have found this way that works

from django.core.files.uploadedfile import SimpleUploadedFile
 ...
def test_form(self):
        upload_file = open('path/to/file', 'rb')
        post_dict = {'title': 'Test Title'}
        file_dict = {'file': SimpleUploadedFile(upload_file.name, upload_file.read())}
        form = MyForm(post_dict, file_dict)
        self.assertTrue(form.is_valid())
Gabriel Hurley
  • 39,690
  • 13
  • 62
  • 88
Jason Christa
  • 12,150
  • 14
  • 58
  • 85
  • 1
    Depending on what your upload code does, you may need to set the `content_type` attribute of upload_file, too – Steve Jalim Aug 05 '13 at 14:49
  • Where content_type is a string like `'image/jpeg'` for a jpg image file for example. – Igor Medeiros Nov 07 '13 at 02:06
  • 1
    I'm getting `TypeError: 'str' does not support the buffer interface`. – Ariel Nov 18 '15 at 15:35
  • 3
    I want to say that this ought to be done using the `with` construct to avoid having to close the `upload_file` explicitly. – Thismatters Sep 30 '18 at 12:46
  • The adding `files=form_data` fixed the issue for me. Not obvious but a part of answer updated for python3: https://stackoverflow.com/a/34276961/4028977 – Rob Apr 05 '20 at 22:40
22

May be this is not quite correct, but I'm creating image file in unit test using StringIO:

imgfile = StringIO('GIF87a\x01\x00\x01\x00\x80\x01\x00\x00\x00\x00ccc,\x00'
                     '\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;')
imgfile.name = 'test_img_file.gif'

response = self.client.post(url, {'file': imgfile})
dragoon
  • 5,601
  • 5
  • 37
  • 55
10

Here's another way that doesn't require you to use an actual image.

EDIT: Updated for Python 3.

from PIL import Image
from io import BytesIO # Python 2: from StringIO import StringIO
from django.core.files.uploadedfile import InMemoryUploadedFile

...

def test_form(self):
    im = Image.new(mode='RGB', size=(200, 200)) # create a new image using PIL
    im_io = BytesIO() # a BytesIO object for saving image
    im.save(im_io, 'JPEG') # save the image to im_io
    im_io.seek(0) # seek to the beginning

    image = InMemoryUploadedFile(
        im_io, None, 'random-name.jpg', 'image/jpeg', len(im_io.getvalue()), None
    )

    post_dict = {'title': 'Test Title'}
    file_dict = {'picture': image}

    form = MyForm(data=post_dict, files=file_dict)
xyres
  • 20,487
  • 3
  • 56
  • 85
  • 4
    Currently exist the next but: `TypeError: string argument expected, got 'bytes'`. I solved it using `BytesIO` instead `StringIO`, without using `im_io.len` because a `BytesIO` objects havent length attribute. – SalahAdDin Jul 12 '16 at 20:21
  • @SalahAdDin Thanks for the heads up. I tested the above code in Python 2.7 and it works fine. Could you please tell me which version of Python and Django are you using? I'd like to correct the code above for future readers. Thanks. – xyres Jul 13 '16 at 06:56
  • 1
    I'm using `django 1.9` and `python 3.5`. – SalahAdDin Jul 13 '16 at 16:47
3

The accepted answer has a downside, i.e. you always have to keep a dummy file in you environment for testing.

Assuming you are working in a team or in production, that is not very good practice. @xyres approach seems cleaner to me. It can be simplified more though.

As Python3 io docks mention, you can do something like:

loaded_file = BytesIO(b"some dummy bcode data: \x00\x01")
loaded_file.name = 'test_file_name.xls'

# and to load it in form
file_dict = {'file': SimpleUploadedFile(loaded_file.name, loaded_file.read())}

Full version

# Python3
from io import BytesIO
from django.core.files.uploadedfile import SimpleUploadedFile

class FormTestCase(TestCase)
    def test_form(self):
        # Simple and Clear
        loaded_file = BytesIO(b"some dummy bcode data: \x00\x01")
        loaded_file.name = 'test_file_name.xls'

        post_dict = {'title': 'Test Title'}
        file_dict = {'file': SimpleUploadedFile(loaded_file.name, loaded_file.read())}
        form = MyForm(post_dict, file_dict)
        self.assertTrue(form.is_valid())

It is also advised to utilize setUpTestData(cls) and setUp(self) class methods for data preparation. I personally found Mozilla's intro to unit testing very informative and straightforward.

Gr3at
  • 330
  • 6
  • 12
1

Combined a few ideas from this and other Stack posts using Python 3. No external file or libraries required.

from django.core.files.uploadedfile import SimpleUploadedFile

png_hex = ['\x89', 'P', 'N', 'G', '\r', '\n', '\x1a', '\n', '\x00',
           '\x00', '\x00', '\r', 'I', 'H', 'D', 'R', '\x00',
           '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01',
           '\x08', '\x02', '\x00', '\x00', '\x00', '\x90',
           'w', 'S', '\xde', '\x00', '\x00', '\x00', '\x06', 'b', 'K',
           'G', 'D', '\x00', '\x00', '\x00', '\x00',
           '\x00', '\x00', '\xf9', 'C', '\xbb', '\x7f', '\x00', '\x00',
           '\x00', '\t', 'p', 'H', 'Y', 's', '\x00',
           '\x00', '\x0e', '\xc3', '\x00', '\x00', '\x0e', '\xc3',
           '\x01', '\xc7', 'o', '\xa8', 'd', '\x00', '\x00',
           '\x00', '\x07', 't', 'I', 'M', 'E', '\x07', '\xe0', '\x05',
           '\r', '\x08', '%', '/', '\xad', '+', 'Z',
           '\x89', '\x00', '\x00', '\x00', '\x0c', 'I', 'D', 'A', 'T',
           '\x08', '\xd7', 'c', '\xf8', '\xff', '\xff',
           '?', '\x00', '\x05', '\xfe', '\x02', '\xfe', '\xdc', '\xcc',
           'Y', '\xe7', '\x00', '\x00', '\x00', '\x00',
           'I', 'E', 'N', 'D', '\xae', 'B', '`', '\x82']

valid_png_bin = str.encode("".join(png_hex))
png = SimpleUploadedFile("test.png", valid_png_bin)

post_dict = {'title': 'Test Title'}
file_dict = {'picture': png}

form = MyForm(data=post_dict, files=file_dict)
smooth-texan
  • 111
  • 3