[EN] Testing a model that have an ImageField

I have a project where there’s a model with a profile imagem 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:

  • It creates a directory at my /tmp/ and set it’s name to the MEDIA_ROOT variable.
  • Using override_settings I change the MEDIA_ROOT setting to this temporary directory.
  • At the tearDownClass I delete this directory.
  • When I create an object of the given model, the image field get the ImageFile returned by get_test_image_file.

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 method 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 that 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:

  • It uses the override_settings just like the other one.
  • It also uses PIL to create a valid image and adds it to a temporary file.
  • At the context manager when I create the temporary file I add an extra argument delete. I did it this way because NamedTemporaryFile by default deletes the file when it’s closed, and we don’t want it.
  • An important thing to note here is the way I open the file for reading before returning it, we need to guarantee that read returns bytes.

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

Advertisements

2 thoughts on “[EN] Testing a model that have an ImageField

  1. # Hi Luiz if you are writing model level test cases then you can try something like this

    from django.test import TestCase
    from django.contrib.auth import get_user_model
    from s3boto.models import Resource # you can create same model in your local its just for sample only
    import tempfile

    class UserTestCase(TestCase):
    def _create_image(self):
    from PIL import Image
    # path=’/Users/Mac/Downloads/’
    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()
    self.name = self.image.name.split(‘.’)[0]
    self.ext = self.image.name.split(‘.’)[1]
    self.resource = Resource(
    name=self.name,
    ext=self.ext,
    bucket=’test’,
    data_source_type=0,
    type=1,
    description=’dddddddd’,
    created_by_id=1
    )
    self.resource.save()

    self.user = get_user_model()(
    username=’ranvijay’,
    email=’contact.ranvijay@gmail.com’,
    password=’welcome’,
    mobile_no=’9999999999′,
    first_name=’ranvijay’,
    last_name=’sachan’,
    date_of_birth=’1988-01-01′,
    profile_picture=self.resource.name
    )
    self.user.save()

    def test_user_model_email_1(self):
    self.assertEqual(self.user.email, ‘contact.ranvijay@gmail.com’)

    def test_user_profile_pic_2(self):
    self.assertEqual(self.user.profile_picture, self.name)

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s