Полезный блог о django, python

инструкции, руководства для изучающих django, python

Тестирование работы с файлами в django

Тестирование работы с файлами в django

Django предоставляет очень удобные классы TestCase, наследованные от unittest.TestCase стандартной библиотеки. Эти обёртки, помимо стандарных возможностей TestCase, заботятся о вещам специфичных для django. В частности они создают тестовую базу данные и очищают её перед каждым тестом. Это позволяет свободно создавать и уничтожать объекты во время тестов, не переживая за то что может повредится основная база.

Помимо работы с текстовыми и числовыми полями модели и их вариациями, часто приходится иметь дело с FileField и ImageField. И тут всё не так просто. Если в процессе теста вы будете создавать файлы и передавать их для сохранения вашим моделям, то эти тестовые файлы будут загружаться в MEDIA_ROOT и не будут оттуда удаляться по окончанию теста как это происходит с тестовой БД.

Есть несколько вариантов как это исправить.

Первый. Создать, если ещё не создан, для тестов отдельный settings файл и указать там в качестве MEDIA_ROOT временную папку.

 #test_settings.py
 import * from base.py
 import os

 MEDIA_ROOT = os.path.join('/tmp', 'myproject_test_media')

или #test_settings.py import * from base.py import tempfile

 MEDIA_ROOT = os.path.join(tempfile.gettempdir(), 'myproject_test_media')

Первый вариант подойдёт для любого linux дистрибутива. Второй более универсальный - tempfile.gettempdir() возвращает временную директорию специфичную для платформы. Теперь файлы, создаваемые в процессе тестов, будут помещаться во временную директорию. Это самый простой подход, но у него есть недостатки. Временная директория очищается с разной периодичностью для разных систем: где то по cron, где то при перезагрузке. А что если ваши тесты сохраняют довольно большое количество медиа файлов(загружают и парсят xml например). Вручную чистить не вариант, как мне кажется, у нас же АВТОматические тесты. Можно написать свою обёртку над джанговским DiscoverTestRunner и при завершении всех тестов очищать MEDIA_ROOT. Но это подходит только если вы пишите тесты для приложения в целом. Если же вы пишите тесты для приложения в понимании django, т.е такое которое может использоваться в другом проекте, то использовать кастомный раннер не получится да и вообще полагаться на очистку MEDIA_ROOT после тестов не стоит. В этом случае правильнее, на мой взгляд, будет использование override_settings в связке с classTearDown методом TestCase.

# test.py
import tempfile
import shutil
from django.test import TestCase, override_settings


@override_settings(MEDIA_ROOT=tempfile.mkdtemp())
class SomeTests(TestCase):

    @classmethod
    def tearDownClass(cls):
        print(settings.MEDIA_ROOT) # some /tmp/tmpdir
        shututil.rmtree(settings.MEDIA_ROOT)
        super().tearDownClass()
        print(settings.MEDIA_ROOT) # actual MEDIA_ROOT

При помощи декоратора override_settings изменяем значение MEDIA_ROOT для нашего TestCase. mkdtemp создаёт и вовращает уникальную временную директорию. Для ёё удаления после тестов используем специальный метод класса tearDownClass. Не забываем вызвать super().tearDownClass так как django использует его для служебных целей, в том числе для того что сбросить переназначенное декоратором значение MEDIA_ROOT. Поэтому же очистка временной директории должна производить обязательно до вызова super() (обратите внимание на вывод print).