Написание и запуск тестов
См. также
учебник по тестированию, справочник по инструментам тестирования и расширенные темы тестирования.
Этот документ разделен на два основных раздела. Сначала мы объясним, как писать тесты с 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 $ ./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 в памяти, кэширование поделятся, поэтому вы можете писать тесты, которые могут обмениваться базой данных между потоками.
END_OF_DOCUMENT_MARKERПолучение данных из вашей производственной базы данных при запуске тестов?
Если ваш код пытается получить доступ к базе данных при компиляции модулей, это произойдёт до настройки тестовой базы данных, что может привести к непредвиденным результатам. Например, если у вас есть запрос к базе данных в коде уровня модуля и реальная база данных существует, данные из производства могут «загрязнить» ваши тесты. В любом случае не рекомендуется иметь запросы к базе данных на этапе импорта в вашем коде — перепишите свой код так, чтобы этого не происходило.
Это также относится к настраиваемым реализациям ready().
См. также
Раздел расширенные темы тестирования с несколькими базами данных.
Порядок выполнения тестов
Для обеспечения того, что весь TestCase код запускается с чистой базой данных, Django test runner переупорядочивает тесты следующим образом:
- Сначала выполняются все подклассы
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 может вставить данные из тестов в кэш живой системы, если вы выполняете тесты в рабочей среде, так как, в отличие от баз данных, отдельный «тестовый кэш» не используется. Это поведение может измениться в будущем.
Понимание вывода тестов
При запуске тестов вы увидите ряд сообщений, пока test runner готовится. Вы можете контролировать уровень детализации этих сообщений с помощью опции verbosity в командной строке:
Creating test database... Creating table myapp_animal Creating table myapp_mineral
Это указывает на то, что test runner создаёт тестовую базу данных, как описано в предыдущем разделе.
После создания тестовой базы данных 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 в скрипте оболочки и вам нужно проверить успех или неудачу на этом уровне.
В более старых версиях код возврата был 0 для непредвиденных успехов.
Ускорение тестов
Запуск тестов параллельно
Пока ваши тесты должным образом изолированы, вы можете запускать их параллельно, чтобы повысить скорость на многоядерном оборудовании. См. 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/4.1/topics/testing/overview/