djangofile-uploadmockingpython-2.xtestcase

Django Test mocked ImageField prevent upload or clean after tests


I am working on a TestCase for my project, involving some Image and FileFields. Some objects are loaded with fixtures, some objects are created during the tests. I Mocked my image with a InMemoryUploadedFile.

When creating a new test object, the mocked image is being uploaded to the MEDIA_ROOT directory, in the correct location defined in the upload_to method.

These mocked images are actually being stored locally during the tests. There is quite some information about how to mock an image. But I could not really find some good information about cleaning up these uploads after tests.

Question:
Is there a possibility to prevent these files from actually being uploaded or is this impossible/dis-encouraged?

or
Should these files being uploaded in favor of the tests (if so, why?), and cleaned afterwards during the tearDown? And what would be an appropriate way to cleanup these uploaded files?

Some code of the current situation I'm working with, cropped to the size to fit the question. I'm not looking for a fully worked-out sample, but just a push in the right direction. Thank you for your time in advance.

Cropped model:

def project_image_upload_location(instance, filename):
    return 'uploads/projects/images/%s' % \
        services.unique_filename(filename)


class ProjectImage(models.Model):
    project = models.ForeignKey(
        'projects.Project')
    name = models.CharField(
        _("Image name"),
        max_length=35)
    image = models.ImageField(
        _("Project image"),
        upload_to=project_image_upload_location,
        width_field='image_width',
        height_field='image_height')
    image_width = models.IntegerField(
        default=0)
    image_height = models.IntegerField(
        default=0)

Cropped TestCase:

from django.core.files.uploadedfile import InMemoryUploadedFile
from django.test import TestCase
from PIL import Image
import StringIO

from projects.models import ProjectImage
from projects import services


class ProjectsTest(TestCase):
    fixtures = ['projects']

    def _create_project_image(self, project, name):
        return ProjectImage.objects.create(
                project=project,
                name=name,
                description="Description",
                image=self._create_mock_image(name="Mocked Image"),
                is_primary=True,
                is_published=True)

        def _get_project_image(self, name):
            return ProjectImage.objects.get(name=name)

    def _create_mock_image(self, name):
        name = name + '.jpg'
        io = StringIO.StringIO()
        size = (200,200)
        color = (255,0,0)
        image = Image.new("RGB", size, color)
        image.save(io, format='JPEG')
        image_file = InMemoryUploadedFile(io, None, name, 'jpeg', io.len, None)
        image_file.seek(0)
        return image_file

    def test_update_project_set_updated_datetime(self):
        project = self._get_project("Project B")
        self.assertEqual(project.updated_datetime, None) 

        project.save()
        self.assertTrue(isinstance(project.updated_datetime, datetime))

Solution

  • I have changed the approach with testing-a-model-that-have-an-imagefield. The image will be uploaded to a temp directory. The guide advises to remove the directory in the tearDown. It might be updated, since the temp/ directory is automatically removed. All tests passed properly.

    Old answer
    I was able to mock the storage with a mock.patch() on default_storage from joeray.me: mocking-files-and-file-storage-for-testing-django-models

    <..crop..>
    import mock
    
    current_storage = 'django.core.files.storage.default_storage._wrapped'
    def _mock_storage():
        return mock.MagicMock(spec=Storage, name="StorageMock")
    
    class ProjectsTest(TestCase):
        fixtures = ['projects']
    
        @mock.patch(current_storage, _mock_storage())
        def _create_project(self, name):
        return Project.objects.create(
                name=name,
                short_description="Short description A",
                full_description="Full description A",
                url="http://test-project-url.com/",
                is_published=True)
    

    Unfortunately I'm not able to test a model which contains an update() within the save() method: django-mocking-storage-on-model-with-update-throws-error