Spec-Zone.ru › Django 6.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 в новом приложении. Этого может быть достаточно, если у вас всего несколько тестов, но по мере роста набора тестов, вероятно, потребуется перестроить его в пакет tests, чтобы разбить тесты на разные подмодули, например 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/

Если файлы тестов названы не по шаблону test*.py, можно задать собственный шаблон имени файла с помощью параметра -p (или --pattern):

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

При запуске тестов используются данные из рабочей базы данных?

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

Это также относится к пользовательским реализациям ready().

См. также

Расширенные темы тестирования с несколькими базами данных.

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

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

  • Сначала запускаются все подклассы TestCase.
  • Затем запускаются все остальные тесты на основе Django (классы тестовых случаев на основе SimpleTestCase, включая TransactionTestCase); их порядок не гарантируется и не задается.
  • После этого запускаются все остальные тесты unittest.TestCase (включая doctest), которые могут изменить базу данных, не восстановив ее исходное состояние.

Примечание

Новый порядок тестов может выявить неожиданные зависимости от порядка их выполнения. Это касается doctest, которые полагались на состояние базы данных, оставленное определенным тестом TransactionTestCase: их необходимо обновить, чтобы они могли выполняться независимо.

Примечание

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

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

Эмуляция отката

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

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

Сторонним приложениям, а также тем, кто разрабатывает приложения для MyISAM, потребуется установить этот параметр; однако в целом следует разрабатывать собственные проекты с использованием транзакционной базы данных и применять TestCase для большинства тестов, поэтому эта настройка вам не понадобится.

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

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

Изменено в Django 5.2:

Для TransactionTestCase сериализованные данные миграций доступны во время setUpClass().

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

Независимо от значения параметра 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 — удобный способ избежать обращения к диску для медиафайлов. Все данные хранятся в памяти, а после выполнения тестов удаляются.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/6.0/topics/testing/overview/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API