I ended up creating a test runner to serve my purpose. I needed to mock the file storage so that images do not actually write to the file system while testing. The images object is being called in many tests thus patching each class would not be DRY
. Also, I noticed that mocking the file itself would leave it on the system in case the test failed. But this method didn't.
I created a file runner.py
in the project root
# runner.py
from unittest.mock import patch
from django.test.runner import DiscoverRunner
from myapp.factories import ImageFactory
class UnitTestRunner(DiscoverRunner):
@patch('django.core.files.storage.FileSystemStorage.save')
def run_tests(self, test_labels, mock_save, extra_tests=None, **kwargs):
mock_save.return_value = ImageFactory.get_image()
return super().run_tests(test_labels, extra_tests=None, **kwargs)
Then I would run my tests using python manage.py tests --testrunner=runner.UnitTestRunner
Just for clarity the ImageFactory.get_image
method is a custom method
from django.core.files.base import ContentFile
from factory.django import DjangoModelFactory
from io import BytesIO
from PIL import Image as PilImage
from random import randint
class ImageFactory(DjangoModelFactory):
@classmethod
def get_image(cls, name='trial', extension='png', size=None):
if size is None:
width = randint(20, 1000)
height = randint(20, 1000)
size = (width, height)
color = (256, 0, 0)
file_obj = BytesIO()
image = PilImage.new("RGBA", size=size, color=color)
image.save(file_obj, extension)
file_obj.seek(0)
return ContentFile(file_obj.read(), f'{name}.{extension}')