Написание и выполнение тестов
См. также
Руководство по тестированию, справочник по инструментам тестирования и подробные темы тестирования.
Данный документ разделен на два основных раздела. Сначала мы объясним, как писать тесты с 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 -Wall manage.py test. Флаг -Wall сообщает Python отображать предупреждения о устаревании. Django, как и многие другие библиотеки Python, использует эти предупреждения для обозначения того, что функции удаляются. Также могут отображаться области в вашем коде, которые не являются строго ошибочными, но могут быть улучшены.
База данных тестов
Тесты, которые требуют базы данных (а именно, тесты моделей), не будут использовать вашу «реальную» (производственную) базу данных. Для тестов создаются отдельные пустые базы данных.
Независимо от того, пройдут тесты или нет, тестовые базы данных удаляются после выполнения всех тестов.
Вы можете предотвратить удаление тестовых баз данных, используя флаг test --keepdb. Это сохраняет тестовую базу данных между запусками. Если база данных не существует, она сначала будет создана. Также будут применены все миграции, чтобы сохранить её актуальной.
Имена тестовых баз данных по умолчанию создаются путём добавления префикса test_ к значению каждого NAME в DATABASES. При использовании SQLite тесты по умолчанию будут использовать базу данных в памяти (то есть база данных будет создана в памяти, полностью избегая файловой системы!). TEST словарь в DATABASES предоставляет ряд настроек для настройки тестовой базы данных. Например, если вы хотите использовать другое имя базы данных, укажите NAME в TEST словаре для любой базы данных в DATABASES.
В PostgreSQL, USER также понадобится доступ для чтения к встроенной postgres базе данных.
Помимо использования отдельной базы данных, исполняющий тесты инструмент будет использовать все остальные настройки базы данных, указанные в файле настроек: ENGINE, USER, HOST и т. д. Тестовая база данных создаётся пользователем, указанным в USER, поэтому вам нужно убедиться, что у данной учётной записи пользователя есть достаточные привилегии для создания новой базы данных в системе.
Для точного управления кодировкой символов вашей тестовой базы данных используйте параметр CHARSET. Если вы используете MySQL, вы также можете использовать параметр COLLATION для управления конкретной сортировкой, используемой тестовой базой данных. Дополнительные сведения об этих и других расширенных настройках см. в документации по настройкам.
При использовании базы данных SQLite в памяти с Python 3.4+ и SQLite 3.7.13+, будет включён общий кэш (shared cache), поэтому вы сможете писать тесты с возможностью совместного использования базы данных между потоками.
Добавлена возможность использования SQLite с общим кэшем (shared cache), как описано выше.
Поиск данных из вашей производственной базы данных при запуске тестов?
Если ваш код пытается получить доступ к базе данных при компиляции своих модулей, это произойдёт *до* настройки тестовой базы данных, что может привести к непредсказуемым результатам. Например, если у вас есть запрос к базе данных в коде уровня модуля и реальная база данных существует, данные производства могут загрязнить ваши тесты. *В любом случае не рекомендуется иметь такие запросы к базе данных во время импорта в вашем коде* — перепишите свой код так, чтобы он этого не делал.
Это также относится к настраиваемым реализациям ready().
См. также
См. подробные темы тестирования с несколькими базами данных.
Порядок выполнения тестов
Для обеспечения того, что весь TestCase код запускается с чистой базой данных, Django test runner переупорядочивает тесты следующим образом:
- Сначала выполняются все подклассы
TestCase. - Затем все другие тесты на основе Django (тестовые случаи, основанные на
SimpleTestCase, включаяTransactionTestCase) выполняются без гарантии или принудительного порядка среди них. - Затем выполняются любые другие тесты
unittest.TestCase(включая doctests), которые могут изменять базу данных без восстановления ее в исходное состояние.
Примечание
Новый порядок выполнения тестов может выявить неожиданные зависимости от порядка тестовых случаев. Это относится к doctests, которые полагались на состояние, оставленное в базе данных данным тестом TransactionTestCase; их необходимо обновить, чтобы они могли выполняться независимо.
Вы можете изменить порядок выполнения внутри групп, передав опцию test --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.assertEqual(future_poll.was_published_recently(), False)
AssertionError: True != 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 любой алгоритм хэширования, используемый в фикстурах, если таковые имеются.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.9/topics/testing/overview/