One of the most important moments when writing a python application is testing. It could be boring, tedious and {{ insert your favorite insult here }}, but it will save your life when that careless teammate goes and twists everything (that careless teammate could be you at 7:00 a.m. on Monday) just before due time. It will also save your life with those tricky bugs, so it is a very good idea to test everything you do (or at least the most of it).

One of the subjects that I have found no useful information about is how to test files. In my case, and in this post, I was testing Images files but it will work with any kind of files, I hope.

First thing First, to not fill the server with garbage

Almost all of the few information I found about this kind of testing involved to create a file every time the test is run and let it there, all alone and lost for the rest of the eternity in one of the folders of the project (I will talk about it later). That kind of behavior I did not like because if my project involved a lot of file testing it would create a lot of garbage at my server and I would need to delete it manually. So I looked for another solution and I find it in the python tempfile library. Using this library, along with override_settings from django.test you may be able to create temporary files in your test, save it in a temporary folder and be sure that eventually your system will delete it. lets do it step by step.

First, let’s write a basic, really basic, model

from django.db import models

class Picture(models.Model):
    picture = models.ImageField()

Then, let’s write a really, really basic, test.

from PIL import Image
import tempfile
from django.test import TestCase
from .models import Picture
from django.test import override_settings

def get_temporary_image(temp_file):
    size = (200, 200)
    color = (255, 0, 0, 0)
    image = Image.new("RGBA", size, color)
    image.save(temp_file, 'jpeg')
    return temp_file

class PictureDummyTest(TestCase):

    @override_settings(MEDIA_ROOT=tempfile.gettempdir())
    def test_dummy_test(self):
            temp_file = tempfile.NamedTemporaryFile()
            test_image = get_temporary_image(temp_file)
            #test_image.seek(0)
            picture = Picture.objects.create(picture=test_image.name)
            print "It Worked!, ", picture.picture
            self.assertEqual(len(Picture.objects.all()), 1)

The first thing to notice is the @override_settings decorator. As its name implies, it allows to override configuration variables that will be used when running the test. In this case, the MEDIA_ROOT, the file system path where Django holds all the uploaded users files. If this path is not overridden, all the created test images will be saved there, filling our system with garbage.

So, The solution I found was to override this path with tempfile.gettempdir(); this tempfile function returns the name of the directory used for temporary files (It will return /tmp, /var/tmp, /usr/tmp or similar, depending on the OS you are using). Doing this, all the test image I create running my tests will be saved in the temporary directory and eventually will be deleted by my OS. That way, I am sure that no garbage will be left behind after the test.

The second thing to notice is tempfile.NamedTemporaryFile(), this tempfile function creates a temporary file with a visible name in the file system (a visible path where it is located). This temporary file is used by get_temporary_image function to create a small red square (It could be brown, it could be blue but I chose red, not that it really matters) and save it in that temporary file. Finally, with

picture = Picture.objects.create(picture=test_image.name)

Im telling Django “Hey, create a Picture instance with test_image as its picture field” Notice that I am using test_image.name to tell Django the path where the test_image is located. After that, Django will take that image and save a copy, related to the Picture instance it just created, at MEDIA_ROOT (that we just overrode).

When running that test, the print response is

It Worked!,  /tmp/tmpIp8YS0

As you can see, the image is saved at /tmp/ so eventually the SO will delete it.

I hope this small tutorial helped you with your tests.

One last thing before I finish. If you need to test images as an attached file in an http request, you just have to use it in the request data without using the .name (because you are attaching the file, not the path where it is located) and use test_image.seek(0) to seek to the frame 0 as if the image has just been open. follow this link for more information about The PIL Image Module.

    
test_image.seek(0)
response = self.client.put(
    self.reverse('upload_user_picture'),
    {'profile_picture': test_image})