Модульные тесты¶
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
Для установки требований, скорее всего, потребуются некоторые пакеты операционной системы, которые не установлены на вашем компьютере. Обычно можно выяснить, какой пакет нужно установить, выполнив поиск в Интернете по последней строке сообщения об ошибке или около того. При необходимости попробуйте добавить свою операционную систему в поисковый запрос.
Если у вас возникли проблемы с установкой требований, вы можете пропустить этот шаг. Смотрите Выполнение всех тестов для получения подробной информации об установке необязательных зависимостей тестов. Если у вас не установлена дополнительная зависимость, тесты, требующие ее установки, будут пропущены.
Для запуска тестов требуется модуль настроек Django, определяющий используемые базы данных. Чтобы помочь вам начать, Django предоставляет и использует пример модуля настроек, который использует базу данных SQLite. Смотрите Использование другого модуля settings, чтобы узнать, как использовать другой модуль настроек для запуска тестов с другой базой данных.
Возникли проблемы? Некоторые распространенные проблемы описаны в разделе Устранение неполадок.
Запуск тестов с использованием tox
¶
Tox - это инструмент для запуска тестов в различных виртуальных средах. Django включает базовый tox.ini
, который автоматизирует некоторые проверки, которые наш сервер сборки выполняет при запросах на поставку. Чтобы запустить модульные тесты и другие проверки (такие как import sorting, documentation spelling checker и code formatting), установите и запустите команду tox
из любого места в дереве исходников Django:
$ python -m pip install tox
$ tox
...\> py -m pip install tox
...\> tox
По умолчанию tox
запускает набор тестов с прилагаемым файлом настроек тестов для SQLite, flake8
, isort
, и проверку орфографии документации. В дополнение к системным зависимостям, отмеченным в других местах этой документации, команда python3
должна быть в вашем пути и связана с соответствующей версией Python. Список окружений по умолчанию можно увидеть следующим образом:
$ tox -l
py3
flake8
docs
isort>=5.1.0
...\> tox -l
py3
flake8
docs
isort>=5.1.0
Тестирование других версий Python и бэкендов баз данных¶
В дополнение к окружению по умолчанию, tox
поддерживает запуск модульных тестов для других версий Python и других бэкендов баз данных. Поскольку набор тестов Django не содержит файла настроек для бэкендов баз данных, отличных от SQLite, вы должны create and provide your own test settings. Например, для запуска тестов на Python 3.7 с использованием PostgreSQL:
$ tox -e py37-postgres -- --settings=my_postgres_settings
...\> tox -e py37-postgres -- --settings=my_postgres_settings
Эта команда устанавливает виртуальное окружение Python 3.7, устанавливает зависимости тестового пакета Django (включая зависимости для PostgreSQL) и вызывает runtests.py
с указанными аргументами (в данном случае --settings=my_postgres_settings
).
Остальная часть этой документации показывает команды для запуска тестов без tox
, однако, любой параметр, переданный в runtests.py
, также может быть передан в tox
, префикснув список аргументов с --
, как указано выше.
Tox также учитывает переменную окружения DJANGO_SETTINGS_MODULE
, если она установлена. Например, следующая команда эквивалентна приведенной выше:
$ 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 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¶
Некоторые тесты требуют наличия Selenium и веб-браузера. Чтобы запустить эти тесты, необходимо установить пакет selenium и запустить тесты с опцией --selenium=<BROWSERS>
. Например, если у вас установлены Firefox и Google Chrome:
$ ./runtests.py --selenium=firefox,chrome
...\> runtests.py --selenium=firefox,chrome
Список доступных браузеров см. в пакете selenium.webdriver.
Указание --selenium
автоматически устанавливает --tags=selenium
для запуска только тех тестов, для которых требуется selenium.
Некоторые браузеры (например, Chrome или Firefox) поддерживают безголовое тестирование, которое может быть быстрее и стабильнее. Добавьте опцию --headless
, чтобы включить этот режим.
Выполнение всех тестов¶
Если вы хотите запустить полный набор тестов, вам потребуется установить ряд зависимостей:
- aiosmtpd
- argon2-cffi 19.1.0+
- asgiref 3.3.2+ (required)
- bcrypt
- colorama
- docutils
- geoip2
- jinja2 2.7+
- numpy
- Pillow 6.2.0+
- PyYAML
- pytz (обязательно)
- сторож
- setuptools
- memcached, плюс supported Python binding
- gettext (gettext в Windows)
- селен
- sqlparse 0.2.2+ (требуется)
- tblib 1.5.0+
Вы можете найти эти зависимости в каталоге pip requirements files внутри каталога tests/requirements
дерева исходников Django и установить их следующим образом:
$ 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
.
Если вы хотите протестировать бэкэнд кэша memcached, вам также нужно определить параметр CACHES
, который указывает на ваш экземпляр memcached.
Чтобы запустить тесты GeoDjango, вам нужно setup 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-приложения¶
Тесты для приложений contrib можно найти в каталоге tests/
, обычно в каталоге <app_name>_tests
. Например, тесты для contrib.auth
находятся в tests/auth_tests
.
Устранение неполадок¶
Набор тестов зависает или показывает сбои на ветке main
¶
Убедитесь, что у вас есть последний точечный релиз 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
Вы также можете попробовать запустить любой набор тестов в обратном порядке, используя опцию --reverse
, чтобы убедиться, что выполнение тестов в другом порядке не вызывает никаких проблем:
$ ./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
.
Советы по написанию тестов¶
Изолирующая регистрация модели¶
Чтобы избежать загрязнения глобального apps
реестра и предотвратить ненужное создание таблиц, модели, определенные в методе тестирования, должны быть привязаны к временному Apps
экземпляру:
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)¶
Поскольку этот шаблон включает в себя много шаблонов, Django предоставляет декоратор isolate_apps()
. Он используется следующим образом:
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
:
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'
...
Декоратор также может быть применен к классам:
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
...
Временный экземпляр Apps
, используемый для изоляции регистрации модели, может быть получен как атрибут при использовании в качестве декоратора класса с помощью параметра attr_name
:
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)
Или в качестве аргумента метода проверки при использовании в качестве декоратора метода с помощью параметра kwarg_name
:
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)