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

Юнит тесты

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

Мы ценим любой вклад в тест кейсы!

Все тесты Django используют инфраструктуру тестирования, которая поставляется с Django. См. Создание и запуск тестов для объяснения того, как писать новые тесты.

Выполнение модульных тестов

Быстрый старт

Сначала создайте форк Django на GitHub.

Затем, создайте и активируйте виртуальную среду. Если вы не знакомы с тем, как это сделать, прочитайте наше руководство по содействию.

Далее клонируйте свою вилку, установите некоторые требования и запустите тесты:

$ git clone https://github.com/YourGitHubName/django.git django-repo
$ cd django-repo/tests
$ python -m pip install -e ..
$ python -m pip install -r requirements/py3.txt
$ ./runtests.py
...\> git clone https://github.com/YourGitHubName/django.git django-repo
...\> cd django-repo\tests
...\> py -m pip install -e ..
...\> py -m pip install -r requirements\py3.txt
...\> runtests.py 

Установка зависимостей, вероятно, потребует некоторых пакетов, которые не установлены на вашем компьютере. Обычно вы можете выяснить, какой пакет установить, выполнив поиск в Интернете по последней строке или около сообщения об ошибке. Добавьте название своей операционной системы в поисковый запрос, если необходимо.

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

Для запуска тестов требуется модуль настроек Django, который определяет используемые базы данных. Чтобы помочь вам начать работу, Django предоставляет пример модуля настроек, который использует базу данных SQLite. См. Использование другого модуля settings, чтобы узнать, как использовать другой модуль настроек для запуска тестов с другой базой данных.

Возникли проблемы? Смотрите Поиск неисправностей для некоторых распространенных проблем.

Запуск тестов с использованием tox

Tox is a tool for running tests in different virtual environments. Django includes a basic tox.ini that automates some checks that our build server performs on pull requests. To run the unit tests and other checks (such as import sorting, the documentation spelling checker, and code formatting), install and run the tox command from any place in the Django source tree:

$ python -m pip install tox
$ tox
...\> py -m pip install tox
...\> tox

By default, tox runs the test suite with the bundled test settings file for SQLite, flake8, isort, and the documentation spelling checker. In addition to the system dependencies noted elsewhere in this documentation, the command python3 must be on your path and linked to the appropriate version of Python. A list of default environments can be seen as follows:

$ tox -l
py3
flake8
docs
isort>=5.1.0
...\> tox -l
py3
flake8
docs
isort>=5.1.0

Тестирование для других версий Python и бэкэндов баз данных

In addition to the default environments, tox supports running unit tests for other versions of Python and other database backends. Since Django’s test suite doesn’t bundle a settings file for database backends other than SQLite, however, you must create and provide your own test settings. For example, to run the tests on Python 3.7 using PostgreSQL:

$ tox -e py37-postgres -- --settings=my_postgres_settings
...\> tox -e py37-postgres -- --settings=my_postgres_settings

This command sets up a Python 3.7 virtual environment, installs Django’s test suite dependencies (including those for PostgreSQL), and calls runtests.py with the supplied arguments (in this case, --settings=my_postgres_settings).

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

Tox also respects the DJANGO_SETTINGS_MODULE environment variable, if set. For example, the following is equivalent to the command above:

$ DJANGO_SETTINGS_MODULE=my_postgres_settings tox -e py35-postgres

Пользователям Windows следует использовать:

...\> set DJANGO_SETTINGS_MODULE=my_postgres_settings
...\> tox -e py35-postgres

Выполнение тестов JavaScript

Django включает набор юнит тестов для JavaScript для функций в некоторых contrib-приложениях. Тесты JavaScript не запускаются по умолчанию с использованием tox, поскольку для них требуется установка Node.js и не являются необходимыми для большинства патчей. Чтобы запустить тесты JavaScript с использованием tox:

$ tox -e javascript
...\> tox -e javascript

Эта команда запускает npm install, чтобы убедиться, что требования для тестов актуальны а затем запускает npm test.

Запуск тестов с использованием django-docker-box

django-docker-box позволяет запускать набор тестов Django во всех поддерживаемых базах данных и версиях Python. Инструкции по установке и использованию см. на странице проекта django-docker-box

Использование другого модуля settings

Модуль настроек по-умолчанию (tests/test_sqlite.py) позволяет вам запускать тестовый набор с использованием SQLite. Если вы хотите запустить тесты с использованием другой базы данных, вам нужно будет определить свой собственный файл настроек. Некоторые тесты, такие как для contrib.postgres, специфичны для конкретной базы данных и будут пропущены, если будут запущены с другим бэкэндом. Некоторые тесты пропускаются или ожидают сбоев на конкретной базе данных (см. DatabaseFeatures.django_test_skips и DatabaseFeatures.django_test_expected_failures на каждом бэкэнде).

Чтобы запустить тесты с разными настройками, убедитесь, что модуль находится в вашей PYTHONPATH и передайте модуль с помощью --settings.

Настройка DATABASES в любом модуле настроек теста должна определять две базы данных:

  • База данных default. Эта база данных должна использовать бэкэнд, который вы хотите использовать для первичного тестирования.

  • База данных с псевдонимом other. База данных other используется для проверки того, что запросы могут быть направлены в разные базы данных. Эта база данных должна использовать тот же бэкэнд, что и default, и у нее должно быть другое имя.

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

  • В параметре USER необходимо указать существующую учетную запись пользователя для базы данных. Этому пользователю необходимо разрешение на выполнение CREATE DATABASE, чтобы можно было создать тестовую базу данных.

  • Опция PASSWORD должна предоставить пароль для USER, который был указан.

Тестовые базы данных получают свои имена путем добавления test_ к значению :настроек настройки:NAME для баз данных, определенных в DATABASES. Эти тестовые базы данных удаляются после завершения тестов.

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

Выполнение только некоторых тестов

Выполнение всего набора тестов Django занимает некоторое время, и выполнение каждого отдельного теста может быть излишним, если, скажем, вы просто добавили тест в Django, который вы хотите запустить быстро, не запуская все остальное. Вы можете запустить подмножество юнит тестов, добавив имена тестовых модулей в runtests.py в командной строке.

Например, если вы хотите запустить тесты только для общих функций и интернационализации, введите:

$ ./runtests.py --settings=path.to.settings generic_relations i18n
...\> runtests.py --settings=path.to.settings generic_relations i18n

Как узнать названия отдельных тестов? Посмотрите в tests/ — каждое имя каталога там — название теста.

Если вы хотите запустить только определенный класс тестов, вы можете указать список путей к отдельным классам тестов. Например, чтобы запустить TranslationTests модуля i18n, введите:

$ ./runtests.py --settings=path.to.settings i18n.tests.TranslationTests
...\> runtests.py --settings=path.to.settings i18n.tests.TranslationTests

Выйдя за рамки этого, вы можете указать индивидуальный метод тестирования, например:

$ ./runtests.py --settings=path.to.settings i18n.tests.TranslationTests.test_lazy_objects
...\> runtests.py --settings=path.to.settings i18n.tests.TranslationTests.test_lazy_objects

Вы можете запустить тесты, начиная с указанного модуля верхнего уровня с помощью опции --start-at. Например:

$ ./runtests.py --start-at=wsgi
...\> runtests.py --start-at=wsgi

Вы также можете запустить тесты, начиная с указанного модуля верхнего уровня с помощью опции --start-after. Например:

$ ./runtests.py --start-after=wsgi
...\> runtests.py --start-after=wsgi

Обратите внимание, что опция --reverse не влияет на опции --start-at или --start-after. Более того, эти опции нельзя использовать с тестовыми метками.

Выполнение тестов Selenium

Some tests require Selenium and a Web browser. To run these tests, you must install the selenium package and run the tests with the --selenium=<BROWSERS> option. For example, if you have Firefox and Google Chrome installed:

$ ./runtests.py --selenium=firefox,chrome
...\> runtests.py --selenium=firefox,chrome

Список доступных браузеров см. в пакете selenium.webdriver.

Указание --selenium автоматически устанавливает --tags=selenium для запуска только тестов, которым требуется Selenium.

Некоторые браузеры (например, Chrome или Firefox) поддерживают headless-тестирование, которое может быть быстрее и стабильнее. Добавьте опцию --headless, чтобы включить этот режим.

Выполнение всех тестов

Если вы хотите запустить полный набор тестов, вам нужно установить ряд зависимостей:

You can find these dependencies in pip requirements files inside the tests/requirements directory of the Django source tree and install them like so:

$ python -m pip install -r tests/requirements/py3.txt
...\> py -m pip install -r tests\requirements\py3.txt

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

Вы также можете установить адаптер(ы) базы данных по вашему выбору с помощью oracle``txt, mysql.txt или postgres.txt.

If you want to test the memcached cache backend, you’ll also need to define a CACHES setting that points at your memcached instance.

To run the GeoDjango tests, you will need to setup a spatial database and install the Geospatial libraries.

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

To run some of the autoreload tests, you’ll need to install the Watchman service.

Покрытие кода

Участникам рекомендуется запустить покрытие в наборе тестов, чтобы определить области, требующие дополнительных тестов. Установка и использование инструмента покрытия описаны в testing code coverage.

Coverage should be run in a single process to obtain accurate statistics. To run coverage on the Django test suite using the standard test settings:

$ coverage run ./runtests.py --settings=test_sqlite --parallel=1
...\> coverage run runtests.py --settings=test_sqlite --parallel=1

After running coverage, generate the html report by running:

$ coverage html
...\> coverage html

При запуске покрытия для тестов Django, файл настроек .coveragerc определяет coverage_html как выходной каталог для отчета, а также исключает несколько каталогов, не имеющих отношения к результатам (тестовый код или код, включенный в Django).

Приложения contrib

Tests for contrib apps can be found in the tests/ directory, typically under <app_name>_tests. For example, tests for contrib.auth are located in tests/auth_tests.

Поиск неисправностей

Тестовый набор зависает или показывает сбои в ветке main

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

On macOS (High Sierra and newer versions), you might see this message logged, after which the tests hang:

objc[42074]: +[__NSPlaceholderDate initialize] may have been in progress in
another thread when fork() was called.

To avoid this set a OBJC_DISABLE_INITIALIZE_FORK_SAFETY environment variable, for example:

$ OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES ./runtests.py

Или добавьте export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES в файл запуска вашей оболочки (например, ~/.profile).

Множество неудачных тестов с UnicodeEncodeError

Если пакет locales не установлен, некоторые тесты завершатся ошибкой UnicodeEncodeError.

Вы можете решить это в системах на базе Debian, например, выполнив:

$ apt-get install locales
$ dpkg-reconfigure locales

Эту проблему можно решить для систем macOS, настроив локаль вашей оболочки:

$ export LANG="en_US.UTF-8"
$ export LC_ALL="en_US.UTF-8"

Запустите команду locale, чтобы подтвердить изменение. При желании добавьте эти команды экспорта в файл запуска вашей оболочки (например, ~/.bashrc для Bash) чтобы избежать необходимости вводить их повторно.

Тесты, которые не работают только в сочетании

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

Опция --bisect runtests.py запустит провальный тест, уменьшая вдвое набор тестов, с которым он запускается на каждой итерации, что часто позволяет идентифицировать небольшое количество тестов, которые могут быть связаны с провалом.

Например, предположим, что провальный тест, который работает сам по себе, это ModelTest.test_eq, тогда:

$ ./runtests.py --bisect basic.tests.ModelTest.test_eq
...\> runtests.py --bisect basic.tests.ModelTest.test_eq

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

Опция --pair запускает заданный тест вместе с каждым другим тестом из набора, позволяя вам проверить, есть ли у другого теста побочные эффекты, которые вызывают неудачу. Итак:

$ ./runtests.py --pair basic.tests.ModelTest.test_eq
...\> runtests.py --pair basic.tests.ModelTest.test_eq

свяжет test_eq с каждой тестовой меткой.

С --bisect и --pair, если вы уже подозреваете, какие случаи могут быть ответственны за сбой, вы можете ограничить тесты для перекрестногоанализа, указав дополнительные метки тестов после первой:

$ ./runtests.py --pair basic.tests.ModelTest.test_eq queries transactions
...\> runtests.py --pair basic.tests.ModelTest.test_eq queries transactions

You can also try running any set of tests in reverse using the --reverse option in order to verify that executing tests in a different order does not cause any trouble:

$ ./runtests.py basic --reverse
...\> runtests.py basic --reverse

Просмотр SQL-запросов, выполняемых во время теста

Если вы хотите изучить SQL, выполняемый в неудачных тестах, вы можете включить SQL logging с помощью параметра --debug-sql. Если вы объедините это с --verbosity=2, все SQL-запросы будут выведены:

$ ./runtests.py basic --debug-sql
...\> runtests.py basic --debug-sql

Просмотр полной трассировки неудачного теста

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

$ ./runtests.py basic --parallel=1
...\> runtests.py basic --parallel=1

Вы также можете использовать переменную окружения DJANGO_TEST_PROCESSES для этой цели.

Советы по написанию тестов

Изоляция регистрации моделей

To avoid polluting the global apps registry and prevent unnecessary table creation, models defined in a test method should be bound to a temporary Apps instance:

from django.apps.registry import Apps
from django.db import models
from django.test import SimpleTestCase

class TestModelDefinition(SimpleTestCase):
    def test_model_definition(self):
        test_apps = Apps(['app_label'])

        class TestModel(models.Model):
            class Meta:
                apps = test_apps
        ...
django.test.utils.isolate_apps(*app_labels, attr_name=None, kwarg_name=None)

Since this pattern involves a lot of boilerplate, Django provides the isolate_apps() decorator. It’s used like this:

from django.db import models
from django.test import SimpleTestCase
from django.test.utils import isolate_apps

class TestModelDefinition(SimpleTestCase):
    @isolate_apps('app_label')
    def test_model_definition(self):
        class TestModel(models.Model):
            pass
        ...

Настройка app_label

Моделям, определенным в тестовом методе без явного app_label автоматически назначается метка приложения, в котором находится их класс тестов.

Чтобы убедиться, что модели, определенные в контексте экземпляров isolate_apps(), установлены правильно, вам необходимо передать набор целевых app_label в качестве аргументов:

tests/app_label/tests.py
from django.db import models
from django.test import SimpleTestCase
from django.test.utils import isolate_apps

class TestModelDefinition(SimpleTestCase):
    @isolate_apps('app_label', 'other_app_label')
    def test_model_definition(self):
        # This model automatically receives app_label='app_label'
        class TestModel(models.Model):
            pass

        class OtherAppModel(models.Model):
            class Meta:
                app_label = 'other_app_label'
        ...

The decorator can also be applied to classes:

from django.db import models
from django.test import SimpleTestCase
from django.test.utils import isolate_apps

@isolate_apps('app_label')
class TestModelDefinition(SimpleTestCase):
    def test_model_definition(self):
        class TestModel(models.Model):
            pass
        ...

The temporary Apps instance used to isolate model registration can be retrieved as an attribute when used as a class decorator by using the attr_name parameter:

from django.db import models
from django.test import SimpleTestCase
from django.test.utils import isolate_apps

@isolate_apps('app_label', attr_name='apps')
class TestModelDefinition(SimpleTestCase):
    def test_model_definition(self):
        class TestModel(models.Model):
            pass
        self.assertIs(self.apps.get_model('app_label', 'TestModel'), TestModel)

Or as an argument on the test method when used as a method decorator by using the kwarg_name parameter:

from django.db import models
from django.test import SimpleTestCase
from django.test.utils import isolate_apps

class TestModelDefinition(SimpleTestCase):
    @isolate_apps('app_label', kwarg_name='apps')
    def test_model_definition(self, apps):
        class TestModel(models.Model):
            pass
        self.assertIs(apps.get_model('app_label', 'TestModel'), TestModel)
Back to Top