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