Написание и выполнение тестов
См. также
Раздел учебника по тестированию, справочник по инструментам тестирования и расширенные темы тестирования.
Этот документ разделен на два основных раздела. Сначала мы объясним, как писать тесты с 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 -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, кэширование данных включено, поэтому вы можете писать тесты с возможностью совместного использования базы данных между потоками.
END_OF_DOCUMENT_MARKERПоиск данных из вашей производственной базы данных во время выполнения тестов?
Если ваш код пытается получить доступ к базе данных при компиляции модулей, это произойдёт до настройки тестовой базы данных, что может привести к непредсказуемым результатам. Например, если у вас есть запрос к базе данных в коде уровня модуля и реальная база данных существует, данные производства могут загрязнить ваши тесты. В любом случае, такие запросы к базе данных во время импорта — плохая идея — перепишите свой код, чтобы этого не происходило.
Это также относится к настраиваемым реализациям ready().
См. также
Раздел дополнительные темы тестирования с несколькими базами данных.
Порядок выполнения тестов
Для гарантии, что весь TestCase код запускается с чистой базой данных, Django-тестовый загрузчик переупорядочивает тесты следующим образом:
- Сначала выполняются все подклассы
TestCase. - Затем выполняются все остальные тесты на основе Django (классы тестовых случаев, основанные на
SimpleTestCase, включаяTransactionTestCase) без гарантированного или принудительного порядка выполнения. - Затем выполняются любые другие тесты
unittest.TestCase(включая доктесты), которые могут изменять базу данных без восстановления её в исходное состояние.
Примечание
Новый порядок выполнения тестов может раскрыть неожиданные зависимости от порядка тестовых случаев. Это имеет место для доктестов, которые полагались на состояние, оставленное в базе данных данным тестом 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 — удобный способ предотвращения доступа к диску для файлов медиа. Все данные хранятся в памяти, а затем удаляются после выполнения тестов.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/5.0/topics/testing/overview/