Spec-Zone.ru › Django 1.8

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

См. также

учебник по тестированию, справочник по инструментам тестирования и расширенные темы по тестированию.

Данный документ разделён на два основных раздела. Сначала мы объясним, как писать тесты с 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, использует эти предупреждения, чтобы отметить, когда функции будут удалены. Также могут быть отмечены области вашего кода, которые не являются строго ошибочными, но могут быть улучшены.

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

Тесты, требующие базы данных (в частности, тестовые тесты моделей), не будут использовать вашу «реальную» (производственную) базу данных. Для тестов создаются отдельные пустые базы данных.

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

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

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

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

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

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

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

Различные параметры в настройке базы данных TEST раньше были отдельными параметрами в словаре настроек базы данных, с префиксом TEST_.

Была добавлена возможность использовать SQLite с кэшированием общего доступа, как описано выше.

Получение данных из вашей производственной базы данных при запуске тестов?

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

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

См. также

Раздел дополнительные темы тестирования с несколькими базами данных.

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

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

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

Примечание

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

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

Эмуляция отката

Любые начальные данные, загруженные в миграции, будут доступны только в тестах TestCase и не будут в тестах TransactionTestCase, а также только на бэкендах, где поддерживаются транзакции (самое важное исключение — MyISAM). Это также верно для тестов, которые полагаются на TransactionTestCase такие как LiveServerTestCase и StaticLiveServerTestCase.

Django может перезагрузить эти данные для каждого тестового случая, установив опцию serialized_rollback в значение True в теле TestCase или TransactionTestCase, но обратите внимание, что это замедлит этот набор тестов примерно в 3 раза.

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

Первоначальная сериализация обычно очень быстрая, но если вы хотите исключить некоторые приложения из этого процесса (и немного ускорить выполнение тестов), вы можете добавить эти приложения в TEST_NON_SERIALIZED_APPS.

Приложения без миграций не затронуты; initial_data фикстуры перезагружаются как обычно.

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

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

Кэши не очищаются после каждого теста, и запуск «manage.py test fooapp» может вставить данные из тестов в кэш активной системы, если вы запускаете тесты в рабочей среде, потому что, в отличие от баз данных, отдельный «кэш тестов» не используется. Это поведение может измениться в будущем.

Понимание вывода тестов

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

Creating test database...
Creating table myapp_animal
Creating table myapp_mineral
Loading 'initial_data' fixtures...
No fixtures found.

Это говорит вам о том, что интерпретатор тестов создает тестовую базу данных, как описано в предыдущем разделе.

После создания тестовой базы данных 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 для получения подробностей.

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

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

В последних версиях Django по умолчанию хэш-функция паролей довольно медленная по своему дизайну. Если в ходе ваших тестов вы аутентифицируете много пользователей, вы можете использовать пользовательский файл настроек и установить параметр 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.8/topics/testing/overview/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API