[Django]-Django testing model with ImageField

115👍

For future users, I’ve solved the problem.
You can mock an ImageField with a SimpleUploadedFile instance.

test.py

from django.core.files.uploadedfile import SimpleUploadedFile

newPhoto.image = SimpleUploadedFile(name='test_image.jpg', content=open(image_path, 'rb').read(), content_type='image/jpeg')

31👍

You can use a temporary file, using tempfile. So you don’t need a real file to do your tests.

import tempfile

image = tempfile.NamedTemporaryFile(suffix=".jpg").name

If you prefer to do manual clean-up, use tempfile.mkstemp() instead.

26👍

Tell the mock library to create a mock object based on Django’s File class

import mock
from django.core.files import File

file_mock = mock.MagicMock(spec=File, name='FileMock')

and then use in your tests

newPhoto.image = file_mock

25👍

If you don’t want to create an actual file in the filesystem, you can use this 37-byte GIF instead, small enough to a be a bytes literal in your code:

from django.core.files.uploadedfile import SimpleUploadedFile

small_gif = (
    b'\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x00\x00\x00\x21\xf9\x04'
    b'\x01\x0a\x00\x01\x00\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02'
    b'\x02\x4c\x01\x00\x3b'
)
uploaded = SimpleUploadedFile('small.gif', small_gif, content_type='image/gif')
👤Flimm

13👍

Solution:

from StringIO import StringIO
# in python 3: from io import StringIO
from PIL import Image
from django.core.files.base import File

And create a static method in your TestCase class:

@staticmethod
def get_image_file(name='test.png', ext='png', size=(50, 50), color=(256, 0, 0)):
    file_obj = StringIO()
    image = Image.new("RGB", size=size, color=color)
    image.save(file_obj, ext)
    file_obj.seek(0)
    return File(file_obj, name=name)

Example:

instance = YourModel(name=value, image=self.get_image_file())

3👍

You can do a few additional things to (1) avoid having to keep a dedicated test image around, and (2) ensure that all test files created during testing are deleted right after:

import shutil
import tempfile

from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase, override_settings

MEDIA_ROOT = tempfile.mkdtemp()

@override_settings(MEDIA_ROOT=MEDIA_ROOT)
class MyTest(TestCase):

    @classmethod
    def tearDownClass(cls):
        shutil.rmtree(MEDIA_ROOT, ignore_errors=True)  # delete the temp dir
        super().tearDownClass()

    def test(self):
        img = SimpleUploadedFile('test.jpg', b'whatevercontentsyouwant')
        # ^-- this will be saved in MEDIA_ROOT
        # do whatever ...

2👍

For someone to try upload-image test with python 3.xx

I fix little with Maxim Panfilov’s excellent answer to make more dummy image with independent name.

from io import BytesIO
from PIL import Image
from django.core.files.base import File

#in your TestCase class:
class TestClass(TestCase):
    @staticmethod
    def get_image_file(name, ext='png', size=(50, 50), color=(256, 0, 0)):
        file_obj = BytesIO()
        image = Image.new("RGBA", size=size, color=color)
        image.save(file_obj, ext)
        file_obj.seek(0)
        return File(file_obj, name=name)

    def test_upload_image(self):
        c= APIClient()
        image1 = self.get_image('image.png')
        image2 = self.get_image('image2.png')
        data = 
            { 
                "image1": iamge1,
                "image2": image2,
            }
        response = c.post('/api_address/', data ) 
        self.assertEqual(response.status_code, 201) 

1👍

My approach how to test model with no intention to pass any useful data:

from django.core.files import File
SomeModel.objects.create(image=File(file=b""))

1👍

I want to offer another testing option, which does not require the presence of a file on the server or local machine. Perhaps it will seem interesting to someone and will be useful in the future.

from .models import Photo
from django.core.files.uploadedfile import SimpleUploadedFile

import requests # pip install requests
from io import BytesIO
from PIL import Image
    
class PhotoTestCase(TestCase):
    
    @classmethod
    def setUpTestData(cls):
        url = "https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png" # for example
        response = requests.get(url)
        im = Image.open(BytesIO(response.content))
        im_bytes = BytesIO()
        im.save(fp=im_bytes, format="PNG", quality=100)
        content = im_bytes.getvalue()
        newPhoto = Photo()
        newPhoto.image = SimpleUploadedFile(
            name='test_image.png',
            content=content,
            content_type='image/jpeg')
        newPhoto.save()

    def test_add_photo(self):
        photo_object = Photo.objects.first()
        self.assertEqual(Photo.objects.count(), 1)
        photo_object.image.delete() # It is mandatory that the file is not saved on the HDD (SSD)

0👍

If you use Factory Boy to generate your test data, that library handles this situation with an ImageField factory.

Here is a complete example. I’m assuming that all of these files are in the same Django app.

models.py example:

from django.db import models


class YourModel(models.Model):
    
    image = models.ImageField(upload_to="files/")

factories.py example:

import factory
from . import models


class YourModelFactory(factory.django.DjangoModelFactory):
    
    class Meta:
        model = models.YourModel
 
    image = factory.Django.ImageField()

tests.py example:

from django import test    
from . import factories


class YourModelTests(test.TestCase):
    
    def test_image_model(self):
        yourmodel = factories.YourModelFactory()
        self.assertIsNotNone(yourmodel.image)

Leave a comment