• 3.1
  • 3.2
  • 6.1
  • Версия документации: 5.0

Создание и запуск тестов

Этот раздел состоит из двух разделов. В первом мы расскажем как писать тесты с Django, во втором - как их запускать

Написание тестов

Юнит тесты для Django используют стандартную библиотеку Python: unittest. Эта библиотека позволяет создавать тесты в ООП стиле.

В примере ниже мы наследуемся от django.test.TestCase, который является классом наследником unittest.TestCase, и запускает каждый тест в отдельной транзакции для их изоляции:

from django.test import TestCase
from myapp.models import Animal


class AnimalTestCase(TestCase):
    def setUp(self):
        Animal.objects.create(name="lion", sound="roar")
        Animal.objects.create(name="cat", sound="meow")

    def test_animals_can_speak(self):
        """Animals that can speak are correctly identified"""
        lion = Animal.objects.get(name="lion")
        cat = Animal.objects.get(name="cat")
        self.assertEqual(lion.speak(), 'The lion says "roar"')
        self.assertEqual(cat.speak(), 'The cat says "meow"')

Когда вы запускаете свои тесты, поведение утилиты тестирования по умолчанию заключается в том, чтобы найти все классы тестовых сценариев (то есть подклассы unittest.TestCase) в любом файле, имя которого начинается с test, автоматически построить набор тестов из этих классов тестовых сценариев и запустить этот набор.

Подробности о unittest смотрите в документации Python.

Где должны находиться тесты?

Шаблон приложения, который использует команда startapp, содержит файл tests.py. Это подходит для приложений, которые содержат не много тестов. Но поре роста количества тестов вы захотите разделить их на модули, например test_models.py, test_views.py, test_forms.py, и т.д. Вы можете использовать любую удобную вам структуру.

Смотрите также Использование средства запуска тестов Django для тестирования повторно используемых приложений..

Предупреждение

Если ваши тесты используют базу данных, убедитесь, что вы наследуетесь от django.test.TestCase, а не unittest.TestCase.

При использовании unittest.TestCase экономится время на создание транзакции и сброса базы данных, но если тесты работают с базой данных, их поведение очень зависит от порядка выполнения тестов. В таком случае тесты, которые работают изолированно, могут не проходить при запуске вместе в другими тестами.

Запуск тестов

После написания тестов запустите их с помощью команды test утилиты manage.py вашего проекта:

$ ./manage.py test

Обнаружение тестов основано на встроенном обнаружении тестов модуля unittest. По умолчанию тесты будут обнаружены в любом файле с именем test*.py в текущем рабочем каталоге.

Вы можете указать конкретные тесты для запуска, указав любое количество «меток тестов» в ./manage.py test. Каждая тестовая метка может представлять собой полный пунктирный путь Python к пакету, модулю, подклассу TestCase или тестовому методу. Например:

# Run all the tests in the animals.tests module
$ ./manage.py test animals.tests

# Run all the tests found within the 'animals' package
$ ./manage.py test animals

# Run just one test case class
$ ./manage.py test animals.tests.AnimalTestCase

# Run just one test method
$ ./manage.py test animals.tests.AnimalTestCase.test_animals_can_speak

Вы также можете указать путь к каталогу для обнаружения тестов в этом каталоге:

$ ./manage.py test animals/

Вы можете указать собственное соответствие шаблону имени файла, используя опцию -p (или --pattern), если ваши тестовые файлы называются иначе, чем шаблон test*.py:

$ ./manage.py test --pattern="tests_*.py"

Если нажать Ctrl-C при выполнении тестов, будет завершен текущий тест и затем остановлено выполнение тестов. После остановки будет выведен результат выполнения тестов и удалена тестовая база данных. Ctrl-C может быть очень полезна, если вы забыли указать опцию --failfast, и не хотите ждать окончания выполнения всех тестов, если один из них уже выполнился с ошибкой.

Если вы не хотите ждать окончания текущего теста, можете нажать Ctrl-C еще раз и выполнение тестов завершиться немедленно. При этом вы не получите результатов выполнения тестов и тестовая база данных не будет удалена.

Выполнение тестов с включенными предупреждениями

Хорошая практика - запускать тесты с включенными предупреждениями Python: python -Wall manage.py test. Флаг -Wall указывает Python показывать предупреждения об использовании устаревшего API. Django, как и многие другие библиотеки Python, использует такие предупреждения для пометки функционала, который в будущем будет удален из Django. Предупреждения могут также использоваться, чтобы обратить внимание на код, который не совсем неправильный, но требует лучшей реализации.

Тестовая база данных

Тесты, которые работают с базой данных (то есть тесты моделей), не будут использовать «настоящую» базу данных. Для этого будет создана отдельная пустая база данных.

Независимо от результата выполнения тестов тестовая база будет удалена после выполнения тестов.

Вы можете защитить тестовые базы данных от уничтожения, добавив флаг test --keepdb к команде запуска теста. Это защитит тестовые базы данных между запусками. Если база данных не существует, сначала она будет создана. Любые миграции также будут применены в соответствующем порядке.

Как описано в предыдущем разделе, если выполнение теста принудительно прервано, тестовая база данных не может быть уничтожена. При следующем запуске вас спросят, хотите ли вы повторно использовать или уничтожить базу данных. Используйте опцию test --noinput, чтобы подавить это приглашение и автоматически уничтожить базу данных. Это может быть полезно при запуске тестов на сервере непрерывной интеграции, где тесты могут быть прерваны, например, из-за тайм-аута.

По умолчанию название тестовой базы данных формируется путем добавления префикса test_ к значению настройки NAME баз данных, указанных в настройке DATABASES. При использовании SQLite тестовая база данных создается в памяти (файловая система не используется!). Если вы хотите использовать другое название для тестовой базы данных, укажите его через NAME в словаре насйтроки TEST для баз данных, указанных в DATABASES.

Для PostgreSQL пользователь из USER должен иметь права на чтение встроенной базы данных postgres.

При создании тестовой базы данных будут использоваться настройки базы данных: ENGINE, USER, HOST, и т.д. Тестовая база данных создается пользователем из USER, убедитесь, что у этого пользователя есть права на создания базы данных.

Для больше контроля кодировок в тестовой базе данных используйте параметр CHARSET настройки TEST. При использовании MySQL можно также указать COLLATION. Подробнее о настройках можно прочитать в соответствующем разделе.

При использовании работающего в оперативной памяти SQLite совместно с SQLite 3.7.13+, будет активирован разделяемый кэш, таким образом, вы сможете создавать тесты с возможностью разделения базы данных между потоками.

Запросы к настоящей базе дынных при выполнении тестов

Если ваш код выполняет запросы к базе данных при импорте модуля, эти запросы будут выполнены перед созданием тестовой базы данных, что может привести к неожиданным результатам. То есть такие запросы выполнятся для настоящей базы данных. В любом случае выполнение запросов при импорте модуля - плохая практика. Избегайте этого.

Это также относиться и к методу ready().

Порядок выполнения тестов

Чтобы код TestCase выполнялся на чистой базе данных, Django выполняет тесты в следующем порядке:

  • Сначала классы-наследники TestCase.

  • Затем все остальные тесты на основе Django (классы тестовых сценариев, основанные на SimpleTestCase, включая TransactionTestCase) запускаются без какого-либо определенного порядка, гарантированного и принудительного.

  • Затем все остальные тесты unittest.TestCase (включая «doctests»), которые могут изменять базу данных без восстановления начального состояния.

Примечание

Такой порядок выполнения тестов может выявить неожиданные зависимости между тестами. Например, «doctests» могут использовать состояние, оставленное определенным TransactionTestCase. Такие тесты следует исправить, чтобы они могли выполняться независимо.

Примечание

Сбои, обнаруженные при нагрузочных тестах, заказываются перед всем вышеперечисленным для более быстрой обратной связи. Сюда входят такие вещи, как тестовые модули, которые не удалось найти или загрузить из-за синтаксических ошибок.

Вы можете рандомизировать и/или изменить порядок выполнения внутри групп, используя опции test --shuffle и --reverse. Это может помочь обеспечить независимость ваших тестов друг от друга.

Эмуляция отката изменений

Любые начальные данные, созданные миграциями, будут доступны только в тестах TestCase, но не в тестах TransactionTestCase, и только, если база данных поддерживает транзакции (наиболее важным исключением будет MyISAM). Это также верно для тестов, которые основаны на TransactionTestCase, таких как LiveServerTestCase и StaticLiveServerTestCase.

Django может восстановить начальные данные перед каждым тестом, эмулируя откат изменений, если указать атрибут serialized_rollback с True в теле TestCase или TransactionTestCase. Но учтите, это замедлит выполнение тестов приблизительно в 3 раза.

Распространяемые приложения и те, что используют MyISAM, должны использовать этот атрибут. Но мы рекомендуем использовать базу данных, которая поддерживает транзакции, и TestCase для создания тестов. В таком случае эта настройка не понадобится.

Начальная сериализация данных обычно работает очень быстро, но если вы хотите исключить некоторые приложения из этого процесса (и немного ускорить выполнение тестов), вы можете указать их в настройке TEST_NON_SERIALIZED_APPS.

Чтобы избежать повторной загрузки сериализованных данных, используйте параметр serialized_rollback=True, который отключает сигнал post_migrate при очистке тестовой базы данных.

Другие условия выполнения тестов

Независимо от значения DEBUG в вашем файле настроек, Django выполняет все тесты со значением DEBUG=False. Таким образом, при выполнении тестов код будет работать так же, как и на боевом сервере.

Кэши не очищаются после каждого теста, и запуск manage.py test fooapp может вставить данные из тестов в кеш работающей системы, если вы запускаете тесты в рабочей среде, поскольку, в отличие от баз данных, отдельный «тестовый кеш» не используется. Такое поведение может измениться в будущем.

Разбираем вывод в консоль при выполнении тестов

При запуске тестов вы увидите ряд сообщений, пока программа запуска тестов готовится. Вы можете контролировать уровень детализации этих сообщений с помощью опции verbosity в командной строке:

Creating test database...
Creating table myapp_animal
Creating table myapp_mineral

Этот вывод уведомляет, что была создана тестовая база данных, как описано выше.

После создания тестовой базы данных Django запустит ваши тесты. Если все пойдет хорошо, вы увидите что-то вроде этого:

----------------------------------------------------------------------
Ran 22 tests in 0.221s

OK

Однако если тесты не пройдены, вы увидите полную информацию о том, какие тесты не пройдены:

======================================================================
FAIL: test_was_published_recently_with_future_poll (polls.tests.PollMethodTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/dev/mysite/polls/tests.py", line 16, in test_was_published_recently_with_future_poll
    self.assertIs(future_poll.was_published_recently(), False)
AssertionError: True is not False

----------------------------------------------------------------------
Ran 1 test in 0.003s

FAILED (failures=1)

Мы не будем подробно описывать информацию об ошибке, но все и так должно быть понятно. Подробности вы можете найти в документации Python для пакета unittest.

Обратите внимание, что код возврата сценария запуска тестов равен 1 для любого количества неудачных тестов (независимо от того, был ли сбой вызван ошибкой, неудачным утверждением или неожиданным успехом). Если все тесты пройдены, код возврата равен 0. Эта функция полезна, если вы используете сценарий запуска тестов в сценарии оболочки и вам необходимо проверить успех или неудачу на этом уровне.

Оптимизация тестов

Запуск тестов параллельно

Если ваши тесты хорошо изолированны, вы можете запускать их параллельно, чтобы получить преимущество в скорости при использовании многоядерных процессоров. Смотрите test --parallel.

Хэширование пароля

При генерации хэша для паролей используется медленный алгоритм. Если вы создаете большое количество пользователей в тестах, можно создать отдельный файл настроек и указать в PASSWORD_HASHERS более быстрый алгоритм:

PASSWORD_HASHERS = [
    "django.contrib.auth.hashers.MD5PasswordHasher",
]

Не забудьте также добавить в PASSWORD_HASHERS все хэширующие алгоритмы, которые используются в фикстурах.

Сохранение тестовой базы данных

Опция test --keepdb сохраняет тестовую базу данных между запусками тестов. Пропускается создание и удаление базы данных, что может значительно ускорить выполнение тестов.

Как избежать доступа к диску для медиафайлов

InMemoryStorage — это удобный способ запретить доступ к диску для медиафайлов. Все данные хранятся в памяти, а после запуска тестов удаляются.

Back to Top