Модульные тесты

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

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

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

Запуск модульных тестов

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

Во-первых, fork Django on GitHub.

Во-вторых, создайте и активируйте виртуальную среду. Если вы не знаете, как это сделать, прочитайте нашу статью contributing tutorial.

Затем клонируйте ваш форк, установите некоторые требования и запустите тесты:

$ 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 

Installing the requirements will likely require some operating system packages that your computer doesn’t have installed. You can usually figure out which package to install by doing a web search for the last line or so of the error message. Try adding your operating system to the search query if needed.

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

Для запуска тестов требуется модуль настроек 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, black, 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
black
flake8>=3.7.0
docs
isort>=5.1.0
...\> tox -l
py3
black
flake8>=3.7.0
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.9 using PostgreSQL:

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

This command sets up a Python 3.9 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 py39-postgres

Пользователи Windows должны использовать:

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

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

Django включает набор JavaScript unit tests для функций в некоторых приложениях 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

The included settings module (tests/test_sqlite.py) allows you to run the test suite using SQLite. If you want to run the tests using a different database, you’ll need to define your own settings file. Some tests, such as those for contrib.postgres, are specific to a particular database backend and will be skipped if run with a different backend. Some tests are skipped or expected failures on a particular database backend (see DatabaseFeatures.django_test_skips and DatabaseFeatures.django_test_expected_failures on each backend).

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

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

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

Вы можете найти эти зависимости в каталоге pip requirements files внутри каталога tests/requirements дерева исходников Django и установить их следующим образом:

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

If you encounter an error during the installation, your system might be missing a dependency for one or more of the Python packages. Consult the failing package’s documentation or search the web with the error message that you encounter.

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

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

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

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

Чтобы запустить некоторые тесты автозагрузки, вам нужно установить службу Watchman.

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

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

Для получения точной статистики покрытие следует запускать в одном процессе. Чтобы запустить покрытие на тестовом наборе Django, используя стандартные настройки тестов:

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

После выполнения покрытия создайте html-отчет:

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

Устранение неполадок

Test suite hangs or shows failures on main branch

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

На macOS (High Sierra и более новые версии) вы можете увидеть следующее сообщение в журнале, после чего тесты зависают:

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

Чтобы избежать этого, установите переменную окружения OBJC_DISABLE_INITIALIZE_FORK_SAFETY, например:

$ 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, если вы уже подозреваете, какие случаи могут быть причиной сбоя, вы можете ограничить тесты для перекрестного анализа с помощью specifying further test labels после первого:

$ ./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 a random or reverse order using the --shuffle and --reverse options. This can help verify that executing tests in a different order does not cause any trouble:

$ ./runtests.py basic --shuffle
$ ./runtests.py basic --reverse
...\> runtests.py basic --shuffle
...\> 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. To do this, use the isolate_apps() decorator:

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'
        ...
Вернуться на верх