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

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

Проблемы ? См. Раздел « Устранение неполадок» для ознакомления с некоторыми распространенными проблемами.

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

Tox - это инструмент для запуска тестов в разных виртуальных средах. Django содержит tox.ini базовый файл, который автоматизирует некоторые проверки, которые наш сервер сборки выполняет для запросов на участие. Чтобы запустить модульные тесты и другие проверки (например, порядок импорта , написание документации и форматирование кода ), установите и запустите команду 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
...\> tox -l
py3
flake8
docs
isort

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

В дополнение к средам по умолчанию tox поддерживает выполнение модульных тестов для других версий Python и движков баз данных. Поскольку набор тестов Django не предоставляет файлы настроек для других баз данных, кроме SQLite, вы должны создать и предоставить свои собственные настройки теста . Например, чтобы запустить тесты с 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 для функций в определенных приложениях 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 , что указаны в , относятся к конкретному ядру базы данных и будут опущены при использовании другой базы данных.

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

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

  • База данных default . При этом должен использоваться двигатель, который вы в первую очередь хотите протестировать.
  • База данных с псевдонимом 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=<NAVIGATEURS> . Например, если установлены Firefox и Google Chrome:

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

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

Индикация --selenium автоматически устанавливает --tags=selenium запуск только тестов, требующих селен.

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

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

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

Вы можете найти эту зависимость в файлах пип зависимости в tests/requirements исходном коде Джанго каталога , и вы можете установить их так:

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

Эти зависимости не являются обязательными. Если один из них отсутствует, соответствующие тесты будут пропущены.

Для запуска некоторых тестов autoreload вам необходимо установить службу Watchman .

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

Участникам предлагается выполнить покрытие кода в наборе тестов, чтобы определить области, требующие дальнейшего тестирования. Установка и использование инструмента покрытия описаны в разделе «Покрытие кода тестирования» .

Чтобы получить правильную статистику, покрытие тестирования должно быть инициировано в одном процессе. Чтобы запустить покрытие в тестовом наборе 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/ , обычно в <nom_app>_tests . Например, тесты для могут contrib.auth быть найдены в tests/auth_tests .

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

Набор тестов зависает или показывает ошибки на master ветке

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

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

$ ./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, используя --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 целей в качестве параметров:

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

Декоратор также может применяться к классам:

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)

Copyright ©2020 All rights reserved