Testing a model that have an ImageField

February 9, 2016

I have a project where there’s a model with a profile image field. This field is required.

I did not want to maintain an image file in the repository just for tests, so I decided to research other solutions.

Imagem file at model’s creation

I’ve created a helper method that returns an ImageField, which then I use at the object’s creation with the model’s manager:

def get_test_image_file():
    from django.core.files.images import ImageFile
    file = tempfile.NamedTemporaryFile(suffix='.png')
    return ImageFile(file, name=file.name)

The tempfile Python’s module have some really handy functions, you should take a look.

MEDIA_ROOT = tempfile.mkdtemp()

@override_settings(MEDIA_ROOT=MEDIA_ROOT)
class MeuPetTest(TestCase):
    @classmethod
    def tearDownClass(cls):
        shutil.rmtree(MEDIA_ROOT, ignore_errors=True)
        super().tearDownClass()

    def create_pet(self):
        return Pet.objects.create(profile_picture=get_test_image_file())

There’s four comments I’d like to make about this code:

This way, even if some file was left without being deleted, at my computer’s reboot the system will erase it by itself, saving me from some manual file deletion.

Testing file upload with post

For tests with the POST requests my approach needs to change a bit, as I’ve declared the profile image field as an ImageField we can see in the source code of Django it makes a few assumptions about the data that will be passed to this field.

To satisfy Django’s validations the solution that best worked for me was this:

@override_settings(MEDIA_ROOT=MEDIA_ROOT)
class PetRegisterTest(TestCase):
    def _create_image(self):
        from PIL import Image

        with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as f:
            image = Image.new('RGB', (200, 200), 'white')
            image.save(f, 'PNG')

        return open(f.name, mode='rb')

    def setUp(self):
        self.image = self._create_image()

    def tearDown(self):
        self.image.close()

    def test_show_registered_page(self):
        response = self.client.post(reverse('pet:register'),
                                    data={'profile_picture': self.image},
                                    follow=True)

A few observations about this code:

This two ways worked really well for me and I hope it also help others people. ;)