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.

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. 😉