Spec-Zone.ru › Django 3.2

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

См. также

учебник по тестированию, справочник по инструментам тестирования и расширенные темы тестирования.

Этот документ разделен на два основных раздела. Сначала мы объясним, как писать тесты с 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
$ ./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 -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, поэтому необходимо убедиться, что данному учётной записи пользователя предоставлены достаточные права для создания новой базы данных в системе.

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

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

END_OF_DOCUMENT_MARKER

Поиск данных из вашей производственной базы данных при запуске тестов?

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

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

См. также

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

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

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

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

Примечание

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

Вы можете изменить порядок выполнения внутри групп, используя опцию test --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 для получения подробностей.

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

Ускорение тестов

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

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

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

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

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

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

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

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

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

Spec-Zone.ru

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