Написание и запуск тестов

Этот документ разделен на два основных раздела. Сначала мы объясним, как писать тесты с помощью Django. Затем мы объясняем, как их выполнять.

Написание тестов

Модульные тесты Django используют unittest стандартный модуль библиотеки Python. Этот модуль определяет тесты в соответствии с подходом на основе классов.

Вот пример класса, наследующего 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 в новом приложении. Это хорошо работает, когда есть всего несколько тестов. Но как только набор тестов растет становится лучше реструктурировать файл в модуле Python для того , чтобы отделить тестирование суб-модули , такие как 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» во всей древовидной структуре из текущего рабочего каталога.

Вы можете выбрать конкретные тесты для запуска, указав «имена тестов» при заказе . Каждое имя теста может быть полным путем Python в указанном синтаксисе к пакету, модулю, подклассу или методу тестирования. Например :./manage.py test 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 отображать предупреждения об устаревании. Django, как и любая другая библиотека Python, использует эти предупреждения для уведомления о том, что некоторые функции скоро исчезнут. Есть также предостережения, которые могут указать на то, что некоторые области кода не являются в корне неправильными, но могут выиграть от лучшей реализации.python -Wa manage.py test -Wa

Тестовая база данных

Тесты, которым нужна база данных (т.е. тесты с моделями), не используют «настоящую» (производственную) базу данных. Отдельные пустые базы данных создаются специально для тестирования.

Независимо от того, пройдены ли тесты или нет, тестовые базы данных удаляются сразу после выполнения всех тестов.

Вы можете избежать отбрасывания тестовых баз данных, используя опцию . Это помогает сохранять тестовую базу данных между сессиями. Если база данных не существует, она все равно будет создана. Любая миграция также будет применяться, чтобы поддерживать базу данных в актуальном состоянии.test --keepdb

Как объяснялось в предыдущем разделе, если тестовый запуск внезапно прерывается, тестовая база данных не может быть уничтожена. При следующем запуске вас спросят, хотите ли вы повторно использовать или уничтожить базу данных. Используйте опцию, чтобы удалить этот вопрос и автоматически уничтожить базу данных. Это может быть полезно при запуске тестов на сервере непрерывной интеграции, где тесты могут быть остановлены, например, по таймауту.test --noinput

Имена баз данных по умолчанию испытаний создаются предваряя значения каждого NAME в DATABASES с test_ . В SQLite тесты по умолчанию используют базу данных в памяти (то есть база данных создается в памяти без использования файловой системы). Словарь TEST в DATABASES предложениях нескольких параметров , чтобы настроить тестовую базу данных. Например, если вы хотите использовать другое имя базы данных, заполните NAME словарь TEST затронутой базы данных DATABASES .

В PostgreSQL USER также требуется доступ для чтения к встроенной базе данных postgres .

Кроме того, создание отдельной базы данных, тест бегун использует одни и те же параметры базы данных из файла настроек: ENGINE , USER , и HOST т.д. Тестовая база данных создается пользователем USER , поэтому вы должны убедиться, что эта учетная запись пользователя имеет необходимые права для создания новой базы данных в вашей системе.

Для более точного управления кодировкой символов в тестовой базе данных используйте параметр CHARSET TEST. С MySQL также можно использовать параметр COLLATION для управления сопоставлением, используемым тестовой базой данных. См. Документацию по настройкам для получения более подробной информации об этих расширенных настройках.

Если вы используете базу данных SQLite в памяти, общий кеш включен, чтобы вы могли писать тесты с возможностью совместного использования базы данных между потоками.

Доступ к данным производственной базы данных во время выполнения тестов?

Если ваш код пытается получить доступ к базе данных во время компиляции кода, это произойдет до того, как база данных будет настроена, с потенциально неожиданными результатами. Например, если запрос к базе данных запускается в коде уровня модуля и существует фактическая база данных, производственные данные могут помешать тестированию. В любом случае настоятельно рекомендуется не размещать запросы к базе данных, выполняемые при импорте кода , это вопрос переписывания кода, чтобы этого не произошло.

Это также относится к пользовательским реализациям ready() .

Порядок выполнения тестов

Чтобы гарантировать, что весь код TestCase начинается с чистой базы данных, средство запуска тестов Django переупорядочивает тесты следующим образом:

  • Все подклассы 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 при сбросе тестовой базы данных.

Другие условия испытаний

Все тесты Django запускаются с DEBUG = False, независимо от значения параметра DEBUG в вашем файле настроек. Это гарантирует, что наблюдаемый результат кода соответствует тому, что фактически будет производиться в производственной среде.

Кеши не очищаются после каждого теста, и запуск «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 любые хэш-алгоритмы, используемые в моментальных снимках, если применимо.

Сохранение тестовой базы

Эта опция сохраняет тестовую базу данных между несколькими запусками. Это игнорирует действия create и destroy и может значительно сократить время выполнения теста.test --keepdb

Copyright ©2020 All rights reserved