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