Написание и выполнение тестов
См. также
Раздел учебника по тестированию, справочник по инструментам тестирования и дополнительные темы по тестированию.
Этот документ разделён на два основных раздела. Сначала мы объясним, как писать тесты с 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. При использовании MySQL вы также можете использовать опцию COLLATION для управления конкретной сортировкой, используемой тестовой базой данных. Подробнее об этих и других расширенных настройках см. в документации по настройкам.
При использовании SQLite в памяти с SQLite кеш общих данных включён, поэтому вы можете писать тесты с возможностью совместного использования базы данных между потоками.
Поиск данных из вашей производственной базы данных при выполнении тестов?
Если ваш код пытается получить доступ к базе данных во время компиляции его модулей, это произойдёт до настройки тестовой базы данных, что может привести к непредвиденным результатам. Например, если у вас есть запрос к базе данных в коде модуля и реальная база данных существует, данные производства могут загрязнить ваши тесты. В любом случае, плохо иметь такие запросы к базе данных во время импорта в вашем коде — перепишите ваш код, чтобы этого не происходило.
Это также относится к настраиваемым реализациям 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. Эта функция полезна, если вы используете скрипт загрузчика тестов в скрипте оболочки и хотите проверить успех или неудачу на этом уровне.
В более старых версиях код возврата был равен 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/4.2/topics/testing/overview/