• 3.1
  • 5.0
  • 6.1
  • Версия документации: 3.2

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

Этот раздел состоит из двух разделов. В первом мы расскажем как писать тесты с 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"')

When you run your tests, the default behavior of the test utility is to find all the test cases (that is, subclasses of unittest.TestCase) in any file whose name begins with test, automatically build a test suite out of those test cases, and run that suite.

Подробности о unittest смотрите в документации Python.

Где должны находиться тесты?

Шаблон приложения, который использует команда startapp, содержит файл tests.py. Это подходит для приложений, которые содержат не много тестов. Но поре роста количества тестов вы захотите разделить их на модули, например test_models.py, test_views.py, test_forms.py, и т.д. Вы можете использовать любую удобную вам структуру.

Смотрите также Использование средства запуска тестов Django для тестирования повторно используемых приложений..

Предупреждение

Если ваши тесты используют базу данных, убедитесь, что вы наследуетесь от django.test.TestCase, а не unittest.TestCase.

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

Запуск тестов

Once you’ve written tests, run them using the test command of your project’s manage.py utility:

$ ./manage.py test

Test discovery is based on the unittest module’s built-in test discovery. By default, this will discover tests in any file named «test*.py» under the current working directory.

You can specify particular tests to run by supplying any number of «test labels» to ./manage.py test. Each test label can be a full Python dotted path to a package, module, TestCase subclass, or test method. For instance:

# 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

You can also provide a path to a directory to discover tests below that directory:

$ ./manage.py test animals/

You can specify a custom filename pattern match using the -p (or --pattern) option, if your test files are named differently from the test*.py pattern:

$ ./manage.py test --pattern="tests_*.py"

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

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

Выполнение тестов с включенными предупреждениями

Хорошая практика - запускать тесты с включенными предупреждениями Python: python -Wall manage.py test. Флаг -Wall указывает Python показывать предупреждения об использовании устаревшего API. Django, как и многие другие библиотеки Python, использует такие предупреждения для пометки функционала, который в будущем будет удален из Django. Предупреждения могут также использоваться, чтобы обратить внимание на код, который не совсем неправильный, но требует лучшей реализации.

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

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

Независимо от результата выполнения тестов тестовая база будет удалена после выполнения тестов.

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

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

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

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

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

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

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

Запросы к настоящей базе дынных при выполнении тестов

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

Это также относиться и к методу ready().

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

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

  • Сначала классы-наследники TestCase.

  • Then, all other Django-based tests (test cases based on SimpleTestCase, including TransactionTestCase) are run with no particular ordering guaranteed nor enforced among them.

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

Примечание

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

You may reverse the execution order inside groups using the test --reverse option. This can help with ensuring your tests are independent from each other.

Эмуляция отката изменений

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

Caches are not cleared after each test, and running «manage.py test fooapp» can insert data from the tests into the cache of a live system if you run your tests in production because, unlike databases, a separate «test cache» is not used. This behavior may change in the future.

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

When you run your tests, you’ll see a number of messages as the test runner prepares itself. You can control the level of detail of these messages with the verbosity option on the command line:

Creating test database...
Creating table myapp_animal
Creating table myapp_mineral

Этот вывод уведомляет, что была создана тестовая база данных, как описано выше.

Once the test database has been created, Django will run your tests. If everything goes well, you’ll see something like this:

----------------------------------------------------------------------
Ran 22 tests in 0.221s

OK

If there are test failures, however, you’ll see full details about which tests failed:

======================================================================
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.

Note that the return code for the test-runner script is 1 for any number of failed and erroneous tests. If all the tests pass, the return code is 0. This feature is useful if you’re using the test-runner script in a shell script and need to test for success or failure at that level.

Оптимизация тестов

Запуск тестов параллельно

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

Хэширование пароля

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

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

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

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

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

Back to Top