Как настроить тестовую среду в Django?

Ответ

Настройка тестовой среды в Django критически важна для обеспечения надежности и скорости выполнения тестов. Основной подход заключается в использовании отдельных конфигурационных файлов и оптимизации работы с базой данных.

1. Отдельные настройки для тестов: Для тестовой среды рекомендуется создавать отдельный файл настроек (например, settings_test.py), который импортирует основные настройки и переопределяет специфичные для тестов параметры. Чаще всего это касается базы данных.

# project/settings_test.py
from .settings import * # Импортируем все из основного файла настроек

# Переопределение базы данных для тестов
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': ':memory:', # Использование временной SQLite базы данных в оперативной памяти для максимальной скорости
    }
}

# Отключение миграций для ускорения тестов (опционально, если миграции не тестируются)
# MIGRATION_MODULES = {app: None for app in INSTALLED_APPS}

# Другие настройки, например, отключение email-бэкенда для предотвращения отправки реальных писем
# EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'

Запуск тестов с этими настройками:

python manage.py test --settings=project.settings_test

Использование :memory: для SQLite значительно ускоряет тесты, так как база данных создается и уничтожается в оперативной памяти для каждого тестового запуска, исключая дисковые операции.

2. Рекомендации и лучшие практики:

  • django.test.TestCase vs SimpleTestCase: Используйте TestCase для тестов, которые взаимодействуют с базой данных, так как он обеспечивает транзакционную изоляцию для каждого теста (каждый тест запускается в своей транзакции, которая откатывается после завершения). SimpleTestCase подходит для тестов, не требующих доступа к БД, например, для тестирования форм или утилит.
  • setUpTestData(): Используйте статический метод setUpTestData() для создания тестовых данных, которые будут использоваться всеми тестами в классе. Эти данные создаются один раз перед запуском всех тестов класса, что значительно быстрее, чем создавать их в setUp() для каждого отдельного теста.
  • Генераторы тестовых данных (factory_boy, mixer): Вместо использования Django-фикстур (которые могут быть громоздкими и сложными в поддержке) применяйте библиотеки, такие как factory_boy или mixer. Они позволяют декларативно определять фабрики для создания моделей, упрощая генерацию разнообразных и реалистичных тестовых данных.
  • Изоляция внешних сервисов: Для тестов, взаимодействующих с внешними API или сервисами, используйте мокирование (unittest.mock или pytest-mock), чтобы избежать реальных сетевых запросов и сделать тесты быстрыми и детерминированными. Это также предотвращает зависимость тестов от доступности внешних систем.

Ответ 18+ 🔞

Да ты посмотри, что эти джанговские мудозвоны выдумали! Просто пиздец, а не настройка. Ну ладно, слушай сюда, сейчас разжую, как бабушке.

1. Отдельный файл настроек — твой спасательный круг, блядь.

Представь, ты на боевом сервере тесты гоняешь, а они тебе всю продовуху в говно разъебали. Чтобы такого не было, делаешь отдельный файлик, например settings_test.py. Тупо копируешь всё из основных настроек, а потом переписываешь то, что для тестов нужно.

# project/settings_test.py
from .settings import *  # Берём всё как есть, ёпта

# А вот тут — магия, блядь. Меняем базу на ту, что в оперативке живёт.
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': ':memory:',  # Во, сука! Вся база в памяти, как у шамана в голове. Быстро, как хуй с горы.
    }
}

# Можно ещё миграции отключить, если они не нужны. Скорость будет — овердохуища.
# MIGRATION_MODULES = {app: None for app in INSTALLED_APPS}

# Чтобы почту не слать нахуй впустую, ставим заглушку.
# EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'

Как это запустить, спросишь? Да элементарно, ёпта!

python manage.py test --settings=project.settings_test

И всё, понеслась! База в памяти, после тестов её как не бывало — чисто, аккуратно, ни одного лишнего файла на диске.

2. Лайфхаки, чтобы не обосраться.

  • TestCase или SimpleTestCase? Вот смотри: если тест лезет в базу — бери django.test.TestCase. Он каждый тест в отдельной транзакции запускает, а потом откатывает, как будто ничего и не было. Чисто, как у кота под хвостом. Если база не нужна — SimpleTestCase, он легче.
  • setUpTestData() — твой лучший друг. Не надо в каждом тесте создавать одни и те же данные, ёбушки-воробушки! Создай их один раз в этом методе, и все тесты в классе будут ими пользоваться. Экономия времени — просто пиздец.
  • Забудь про фикстуры, как про страшный сон. Это жесть, блядь, их поддерживать. Бери factory_boy или mixer. Написал один раз фабрику — и плоди тестовые объекты, как кроликов. Красота, а не жизнь.
  • Внешние API — нахуй не упали. Твои тесты не должны зависеть от того, упал ли какой-то сторонний сервис. Мокай всё, что шевелится! Библиотека unittest.mock — твоя бро. Подменил ответ — и тест летает, не отвлекаясь на всякую хуйню в интернетах.

Вот и вся наука. Не усложняй, делай по уму, и тесты будут быстрыми, как ядрёна вошь, и надёжными, как швейцарские часы. Почти.