【问题标题】:Django testing model with ImageField使用 ImageField 的 Django 测试模型
【发布时间】:2014-12-05 14:06:35
【问题描述】:

我需要测试我的 Django 应用程序的照片模型。如何使用测试图像文件模拟 ImageField?

tests.py

class PhotoTestCase(TestCase):

    def test_add_photo(self):
        newPhoto = Photo()
        newPhoto.image = # ??????
        newPhoto.save()
        self.assertEqual(Photo.objects.count(), 1)

【问题讨论】:

  • This question 可能有一些相关的见解。
  • 谢谢,但我想不出用它来解决我的问题 :(

标签: python django testing mocking


【解决方案1】:

对于未来的用户,我已经解决了这个问题。 您可以使用 SimpleUploadedFile 实例模拟 ImageField

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')

【讨论】:

  • 完美,应该是公认的答案。在 1.10 中工作,谢谢!
  • @Norak 通过一些实验,我发现name 是您要用于读取的图像的文件名,image_path 是图像的路径。例如/path/to/image.jpg
  • 设置 content_type 是可选的。
  • 这种方式可行,但每次我运行测试时,都会在 /media 目录中创建新的图像文件。
【解决方案2】:

您可以使用临时文件,使用tempfile。所以你不需要一个真实的文件来做你的测试。

import tempfile

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

如果您更喜欢手动清理,请改用tempfile.mkstemp()

【讨论】:

    【解决方案3】:

    告诉 mock 库根据 Django 的 File 类创建一个 mock 对象

    import mock
    from django.core.files import File
    
    file_mock = mock.MagicMock(spec=File, name='FileMock')
    

    然后在你的测试中使用

    newPhoto.image = file_mock
    

    【讨论】:

    • 那么你可以从任意位置打开任意图像newPhoto.image = open(/path/to/foo.jpg)
    • 但如果我使用模拟模块总是这样?
    • Mock 现在是 Python 的标准 Python 库>=3.3 所以不要认为这个第三方库在 Python 中是什么奇怪的东西。
    • @ChillarAnand 我用你的例子,但我有一个错误 - TypeError: 'MagicMock' does not support the buffer interface
    【解决方案4】:

    如果你不想在文件系统中创建一个实际的文件,你可以使用这个 37 字节的 GIF,小到足以在你的代码中成为字节文字:

    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')
    

    【讨论】:

    • 这会产生一个白色像素。我发现了另一个 gif,甚至更小(只有 35 个字节),它产生一个黑色像素。如果有人有兴趣,那就是:b'\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x05\x04\x04\x00\x00\x00\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3b'
    【解决方案5】:

    解决方案:

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

    并在您的 TestCase 类中创建一个静态方法:

    @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)
    

    例子:

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

    【讨论】:

    • 感谢您的回答。请注意,对于 python 3:from io import StringIO
    • 对于 Python 3,我认为是 from io import BytesIO 而不是 StringIO
    【解决方案6】:

    您可以做一些额外的事情来 (1) 避免保留专用的测试映像,以及 (2) 确保在测试期间创建的所有测试文件在之后立即被删除:

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

    【讨论】:

      【解决方案7】:

      我的方法是如何在不传递任何有用数据的情况下测试模型:

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

      【讨论】:

        【解决方案8】:

        有人尝试使用 python 3.xx 进行上传图像测试

        我很少用 Maxim Panfilov 的出色回答来制作更多具有独立名称的虚拟图像。

        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) 
        

        【讨论】:

          【解决方案9】:

          如果您使用Factory Boy 生成测试数据,该库会使用ImageField factory 处理这种情况。

          这是一个完整的例子。我假设所有这些文件都在同一个 Django 应用程序中。

          models.py 示例:

          from django.db import models
          
          
          class YourModel(models.Model):
              
              image = models.ImageField(upload_to="files/")
          

          factories.py 示例:

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

          tests.py 示例:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-04-24
            • 2023-03-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-01-04
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多