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

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

Вы можете указать конкретные тесты для запуска, предоставив им любое количество «тестовых меток» . Каждая тестовая метка может быть полным пунктирным путем Python к пакету, модулю, подклассу или методу тестирования. Например:./manage.py testTestCase

# 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

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

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

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

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

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

Находите данные из производственной базы данных при запуске тестов?

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

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

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

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

  • Все TestCaseподклассы запускаются первыми.
  • Затем все другие тесты на основе Django (в SimpleTestCaseтом числе тестовые примеры TransactionTestCase) запускаются без какого-либо гарантированного или принудительного упорядочивания среди них.
  • Затем unittest.TestCaseзапускаются любые другие тесты (включая doctest), которые могут изменить базу данных без восстановления ее исходного состояния.

Примечание

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

Понимание результатов теста

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

Полное объяснение этого вывода ошибки выходит за рамки этого документа, но оно довольно интуитивно понятно. Вы можете обратиться к документации unittestбиблиотеки Python за подробностями.

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

Ускорение тестов

Параллельное выполнение тестов

Пока ваши тесты должным образом изолированы, вы можете запускать их параллельно, чтобы ускорить работу на многоядерном оборудовании. Смотрите .test --parallel

Хеширование паролей

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

PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.MD5PasswordHasher',
]

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

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

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

Copyright ©2021 All rights reserved