Расширенные темы тестирования
Фабрика запросов
-
class RequestFactory[source]
Фабрика запросов RequestFactory имеет тот же API, что и клиент тестирования. Однако вместо имитации поведения браузера, RequestFactory предоставляет способ генерации экземпляра запроса, который может быть использован в качестве первого аргумента для любого представления. Это означает, что вы можете протестировать функцию представления так же, как и любую другую функцию — как «черный ящик» с точно известными входными данными, проверяя определенные выходные данные.
API фабрики запросов RequestFactory представляет собой немного ограниченный подмножество API клиента тестирования:
- Она имеет доступ только к методам HTTP
get(),post(),put(),delete(),head(),options()иtrace(). - Эти методы принимают все те же аргументы, кроме
follows. Поскольку это просто фабрика для создания запросов, обработка ответа возлагается на вас. - Она не поддерживает middleware. Атрибуты сессии и аутентификации должны быть предоставлены самим тестом, если они необходимы для корректной работы представления.
Пример
Следующий пример — это простой юнит-тест, использующий фабрику запросов:
from django.contrib.auth.models import AnonymousUser, User
from django.test import TestCase, RequestFactory
from .views import MyView, my_view
class SimpleTest(TestCase):
def setUp(self):
# Every test needs access to the request factory.
self.factory = RequestFactory()
self.user = User.objects.create_user(
username='jacob', email='jacob@…', password='top_secret')
def test_details(self):
# Create an instance of a GET request.
request = self.factory.get('/customer/details')
# Recall that middleware are not supported. You can simulate a
# logged-in user by setting request.user manually.
request.user = self.user
# Or you can simulate an anonymous user by setting request.user to
# an AnonymousUser instance.
request.user = AnonymousUser()
# Test my_view() as if it were deployed at /customer/details
response = my_view(request)
# Use this syntax for class-based views.
response = MyView.as_view()(request)
self.assertEqual(response.status_code, 200)
Тестирование и несколько баз данных
Тестирование конфигураций primary/replica
Если вы тестируете конфигурацию с несколькими базами данных и репликацией primary/replica (в некоторых базах данных она называется master/slave), эта стратегия создания тестовых баз данных создаёт проблему. Когда тестовые базы данных создаются, репликация отсутствует, и в результате данные, созданные на primary, не будут видны на replica.
Чтобы компенсировать это, Django позволяет определять базу данных как тестовое зеркало. Рассмотрим следующий (упрощённый) пример конфигурации базы данных:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'myproject',
'HOST': 'dbprimary',
# ... plus some other settings
},
'replica': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'myproject',
'HOST': 'dbreplica',
'TEST': {
'MIRROR': 'default',
},
# ... plus some other settings
}
}
В этой настройке у нас есть два сервера баз данных: dbprimary, описываемый алиасом базы данных default, и dbreplica с алиасом replica. Как можно ожидать, dbreplica был настроен администратором базы данных как реплики чтения dbprimary, поэтому в нормальном режиме работы любые записи в default будут появляться в replica.
Если Django создаст две независимые тестовые базы данных, это нарушит любые тесты, которые ожидают репликации. Однако база данных replica настроена как тестовое зеркало (используя тестовую настройку MIRROR), что означает, что во время тестирования replica должна рассматриваться как зеркало default.
При настройке тестовой среды тестовая версия replica не будет создана. Вместо этого подключение к replica будет перенаправлено на default. В результате записи в default будут отображаться в replica — но поскольку это фактически та же база данных, не из-за репликации данных между базами данных.
Управление порядком создания тестовых баз данных
По умолчанию Django предполагает, что все базы данных зависят от базы данных default и, следовательно, всегда создаёт базу данных default первой. Однако никакие гарантии не даются относительно порядка создания других баз данных в вашей тестовой настройке.
Если ваша конфигурация базы данных требует определённого порядка создания, вы можете указать существующие зависимости, используя тестовую настройку DEPENDENCIES. Рассмотрим следующий (упрощённый) пример конфигурации базы данных:
DATABASES = {
'default': {
# ... db settings
'TEST': {
'DEPENDENCIES': ['diamonds'],
},
},
'diamonds': {
... db settings
'TEST': {
'DEPENDENCIES': [],
},
},
'clubs': {
# ... db settings
'TEST': {
'DEPENDENCIES': ['diamonds'],
},
},
'spades': {
# ... db settings
'TEST': {
'DEPENDENCIES': ['diamonds', 'hearts'],
},
},
'hearts': {
# ... db settings
'TEST': {
'DEPENDENCIES': ['diamonds', 'clubs'],
},
}
}
В этой конфигурации база данных diamonds будет создана первой, поскольку это единственный алиас базы данных без зависимостей. Алиасы default и clubs будут созданы следующими (хотя порядок создания этой пары не гарантируется), затем hearts, и, наконец, spades.
Если в определении DEPENDENCIES есть циклические зависимости, будет поднято исключение ImproperlyConfigured.
Расширенные возможности TransactionTestCase
-
TransactionTestCase.available_apps -
Предупреждение
Этот атрибут — частный API. Он может быть изменён или удалён без периода устаревания в будущем, например, для адаптации к изменениям в загрузке приложений.
Он используется для оптимизации собственного набора тестов Django, который содержит сотни моделей, но без взаимосвязей между моделями в разных приложениях.
По умолчанию
available_appsустановлено вNone. После каждого теста Django вызываетflushдля сброса состояния базы данных. Это очищает все таблицы и генерирует сигналpost_migrate, который пересоздаёт один тип содержимого и три разрешения для каждой модели. Эта операция становится дорогостоящей пропорционально числу моделей.Установка
available_appsв список приложений сообщает Django действовать так, как если бы были доступны только модели из этих приложений. ПоведениеTransactionTestCaseизменяется следующим образом:-
post_migrateзапускается перед каждым тестом, чтобы создать типы содержимого и разрешения для каждой модели в доступных приложениях, если они отсутствуют. - После каждого теста Django очищает только таблицы, соответствующие моделям в доступных приложениях. Однако на уровне базы данных усечение может каскадироваться на связанные модели в недоступных приложениях. Кроме того,
post_migrateне запускается; он будет запущен следующимTransactionTestCase, после выбора правильного набора приложений.
Поскольку база данных не полностью очищается, если тест создаёт экземпляры моделей, не включённых в
available_apps, они останутся и могут привести к сбоям в не связанных тестах. Будьте осторожны с тестами, использующими сессии; движок сессий по умолчанию хранит их в базе данных.Поскольку
post_migrateне отправляется после очистки базы данных, её состояние послеTransactionTestCaseне такое, как послеTestCase: в нём отсутствуют строки, созданные обработчикамиpost_migrate. Учитывая порядок выполнения тестов, это не проблема, если либо всеTransactionTestCaseв заданном наборе тестов объявляютavailable_apps, либо ни один из них.available_appsобязательно в собственном наборе тестов Django. -
-
TransactionTestCase.reset_sequences -
Установка
reset_sequences = TrueнаTransactionTestCaseобеспечит, что последовательности всегда будут сбрасываться перед запуском теста:class TestsThatDependsOnPrimaryKeySequences(TransactionTestCase): reset_sequences = True def test_animal_pk(self): lion = Animal.objects.create(name="lion", sound="roar") # lion.pk is guaranteed to always be 1 self.assertEqual(lion.pk, 1)Если вы не тестируете явно номера последовательностей первичных ключей, рекомендуется не жестко кодировать значения первичных ключей в тестах.
Использование
reset_sequences = Trueзамедлит тест, так как сброс первичного ключа — относительно дорогостоящая операция для базы данных.
Использование исполнителя тестов Django для тестирования многократно используемых приложений
Если вы пишете многократно используемое приложение, вы можете использовать исполнителя тестов Django для запуска собственного набора тестов и тем самым воспользоваться инфраструктурой тестирования Django.
Обычная практика — это каталог tests рядом с кодом приложения со следующей структурой:
runtests.py
polls/
__init__.py
models.py
...
tests/
__init__.py
models.py
test_settings.py
tests.py
Давайте посмотрим внутрь нескольких из этих файлов:
#!/usr/bin/env python
import os
import sys
import django
from django.conf import settings
from django.test.utils import get_runner
if __name__ == "__main__":
os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.test_settings'
django.setup()
TestRunner = get_runner(settings)
test_runner = TestRunner()
failures = test_runner.run_tests(["tests"])
sys.exit(bool(failures))
Это сценарий, который вы вызываете для запуска набора тестов. Он настраивает среду Django, создает тестовую базу данных и запускает тесты.
Для ясности, этот пример содержит только минимум, необходимый для использования исполнителя тестов Django. Вы можете добавить параметры командной строки для управления подробностью, передачи определенных меток тестов для запуска и т. д.
SECRET_KEY = 'fake-key'
INSTALLED_APPS = [
"tests",
]
Этот файл содержит настройки Django, необходимые для запуска тестов вашего приложения.
Опять же, это минимальный пример; ваши тесты могут потребовать дополнительных настроек для запуска.
Поскольку пакет tests включён в INSTALLED_APPS при запуске тестов, вы можете определить модели только для тестов в файле models.py.
Использование других фреймворков тестирования
Очевидно, что unittest не является единственной системой тестирования Python. Хотя Django не предоставляет явной поддержки альтернативных фреймворков, он позволяет вызывать тесты, построенные для альтернативного фреймворка, как если бы они были обычными тестами Django.
Когда вы запускаете ./manage.py test, Django обращается к настройке TEST_RUNNER, чтобы определить, что делать. По умолчанию, TEST_RUNNER указывает на 'django.test.runner.DiscoverRunner'. Этот класс определяет стандартное поведение тестирования Django. Это поведение включает:
- Выполнение глобальной подготовки перед тестами.
- Поиск тестов в любом файле ниже текущего каталога, имя которого соответствует шаблону
test*.py. - Создание тестовых баз данных.
- Запуск
migrateдля установки моделей и начальных данных в тестовые базы данных. - Запуск найденных тестов.
- Удаление тестовых баз данных.
- Выполнение глобальной завершающей подготовки после тестов.
Если вы определите собственный класс исполнителя тестов и укажете TEST_RUNNER на этот класс, Django будет запускать ваш исполнитель тестов всякий раз, когда вы запускаете ./manage.py test. Таким образом, можно использовать любой фреймворк для тестирования, который можно выполнить из кода Python, или изменить процесс выполнения тестов Django, чтобы удовлетворить любые ваши потребности в тестировании.
Определение исполнителя тестов
Исполнитель тестов — это класс, определяющий метод run_tests(). Django поставляется с классом DiscoverRunner, который определяет стандартное поведение тестирования Django. Этот класс определяет run_tests() точку входа, а также ряд других методов, которые используются run_tests() для настройки, выполнения и завершения набора тестов.
-
class DiscoverRunner(pattern='test*.py', top_level=None, verbosity=1, interactive=True, failfast=False, keepdb=False, reverse=False, debug_sql=False, **kwargs)[source] -
DiscoverRunnerбудет искать тесты в любом файле, соответствующем шаблонуpattern.top_levelможно использовать для указания каталога, содержащего ваши основные модули Python. Обычно Django может определить это автоматически, поэтому нет необходимости указывать этот параметр. При указании, он должен, как правило, быть каталогом, содержащим ваш файлmanage.py.verbosityопределяет количество уведомлений и отладочной информации, которые будут выведены в консоль;0— отсутствие вывода,1— стандартный вывод, а2— подробный вывод.Если
interactiveимеет значениеTrue, набор тестов имеет право запросить у пользователя инструкции при выполнении набора тестов. Примером такого поведения является запрос разрешения на удаление существующей тестовой базы данных. Еслиinteractiveимеет значениеFalse, набор тестов должен быть в состоянии работать без ручного вмешательства.Если
failfastимеет значениеTrue, набор тестов остановит выполнение после обнаружения первого сбоя теста.Если
keepdbимеет значениеTrue, набор тестов будет использовать существующую базу данных или создать её, если необходимо. ЕслиFalse, будет создана новая база данных, запросив у пользователя удалить существующую, если она есть.Если
reverseимеет значениеTrue, тестовые случаи будут выполнены в обратном порядке. Это может быть полезно для отладки тестов, которые не изолированы должным образом и имеют побочные эффекты. Группировка по тестовому классу сохраняется при использовании этого параметра.Если
debug_sqlимеет значениеTrue, не пройденные тестовые случаи будут выводить SQL-запросы, записанные в журнал django.db.backends, а также трассировку стека. Еслиverbosityимеет значение2, запросы всех тестов выводятся.Django время от времени может расширить возможности исполнителя тестов, добавив новые аргументы. Декларация
**kwargsпозволяет это расширение. Если вы создаете подклассDiscoverRunnerили создаете собственного исполнителя тестов, убедитесь, что он принимает**kwargs.Ваш исполнитель тестов также может определить дополнительные параметры командной строки. Создайте или переопределите метод класса
add_arguments(cls, parser)и добавьте пользовательские аргументы, вызвавparser.add_argument(), чтобы командаtestмогла использовать эти аргументы.Ранее вам нужно было предоставить атрибут
option_listподклассу исполнителя тестов, чтобы добавить параметры в список параметров командной строки, которые могла использовать командаtest.Были добавлены аргументы
keepdb,reverseиdebug_sql.
Атрибуты
-
DiscoverRunner.test_suite -
Класс, используемый для построения набора тестов. По умолчанию он установлен в
unittest.TestSuite. Это можно переопределить, если вы хотите реализовать другую логику для сбора тестов.
-
DiscoverRunner.test_runner -
Это класс низкоуровневого исполнителя тестов, который используется для выполнения отдельных тестов и форматирования результатов. По умолчанию он установлен в
unittest.TextTestRunner. Несмотря на неудачное сходство в соглашениях об именовании, это не тот же тип класса, что иDiscoverRunner, который охватывает более широкий спектр задач. Вы можете переопределить этот атрибут, чтобы изменить способ выполнения и отчета о результатах тестов.
-
DiscoverRunner.test_loader -
Это класс, который загружает тесты, будь то из TestCases, модулей или иным образом, и объединяет их в наборы тестов для выполнения исполнителем. По умолчанию он установлен в
unittest.defaultTestLoader. Вы можете переопределить этот атрибут, если ваши тесты будут загружаться необычным образом.
-
DiscoverRunner.option_list -
Это кортеж параметров
optparse, которые будут переданы вOptionParserкоманды управления для парсинга аргументов. Подробнее см. документацию для модуля Pythonoptparse.Устарело начиная с версии 1.8: Теперь вы должны переопределить метод класса
add_arguments(), чтобы добавить пользовательские аргументы, которые принимаются командой управленияtest.
Методы
-
DiscoverRunner.run_tests(test_labels, extra_tests=None, **kwargs)[source] -
Запустить набор тестов.
test_labelsпозволяет указать, какие тесты необходимо запустить, и поддерживает несколько форматов (см.DiscoverRunner.build_suite()для списка поддерживаемых форматов).extra_tests— список дополнительных экземпляровTestCaseдля добавления в набор, который выполняется исполнителем тестов. Эти дополнительные тесты выполняются дополнительно к тем, которые обнаружены в модулях, перечисленных вtest_labels.Этот метод должен возвращать количество не пройденных тестов.
-
classmethod DiscoverRunner.add_arguments(parser)[source] -
Переопределите этот метод класса, чтобы добавить пользовательские аргументы, принимаемые командой управления
test. Подробнее см.argparse.ArgumentParser.add_argument().
-
DiscoverRunner.setup_test_environment(**kwargs)[source] -
Настраивает тестовую среду, вызывая
setup_test_environment()и устанавливаяDEBUGвFalse.
-
DiscoverRunner.build_suite(test_labels, extra_tests=None, **kwargs)[source] -
Создает набор тестов, соответствующий предоставленным меткам тестов.
test_labels— список строк, описывающих тесты для выполнения. Метка теста может иметь одну из четырёх форм:-
path.to.test_module.TestCase.test_method— Запуск одного метода теста в тестовом случае. -
path.to.test_module.TestCase— Запуск всех методов теста в тестовом случае. -
path.to.module— Поиск и запуск всех тестов в указанном пакете или модуле Python. -
path/to/directory— Поиск и запуск всех тестов в указанной директории.
Если
test_labelsимеет значениеNone, исполнитель тестов будет искать тесты во всех файлах ниже текущей директории, имена которых соответствуют его шаблонуpattern(см. выше).extra_tests— список дополнительных экземпляровTestCaseдля добавления в набор, который выполняется исполнителем тестов. Эти дополнительные тесты выполняются дополнительно к тем, которые обнаружены в модулях, перечисленных вtest_labels.Возвращает экземпляр
TestSuite, готовый к выполнению. -
-
DiscoverRunner.setup_databases(**kwargs)[source] -
Создаёт тестовые базы данных.
Возвращает структуру данных, содержащую достаточную информацию для отмены внесённых изменений. Эти данные будут переданы функции
teardown_databases()по завершении тестирования.
-
DiscoverRunner.run_suite(suite, **kwargs)[source] -
Выполняет набор тестов.
Возвращает результат, полученный при выполнении набора тестов.
-
DiscoverRunner.teardown_databases(old_config, **kwargs)[source] -
Удаляет тестовые базы данных, восстанавливая условия до начала тестирования.
old_config— это структура данных, определяющая изменения в конфигурации базы данных, которые необходимо отменить. Она является результатом выполнения методаsetup_databases().
-
DiscoverRunner.teardown_test_environment(**kwargs)[source] -
Восстанавливает среду до состояния до начала тестирования.
-
DiscoverRunner.suite_result(suite, result, **kwargs)[source] -
Вычисляет и возвращает код возврата, основанный на наборе тестов и результате их выполнения.
Средства тестирования
django.test.utils
Для помощи в создании собственного исполнителя тестов Django предоставляет ряд служебных методов в модуле django.test.utils.
-
setup_test_environment()[source] -
Выполняет любую глобальную настройку перед запуском тестов, например, устанавливает инструменты для отслеживания рендеринга шаблонов и настраивает буфер для тестовых сообщений электронной почты.
-
teardown_test_environment()[source] -
Выполняет любую глобальную очистку после выполнения тестов, например, удаляет специальные связи с системой шаблонов и восстанавливает обычные сервисы электронной почты.
django.db.connection.creation
Модуль создания бэкенда базы данных также предоставляет некоторые средства, которые могут быть полезны при тестировании.
-
create_test_db(verbosity=1, autoclobber=False, serialize=True, keepdb=False) -
Создаёт новую тестовую базу данных и выполняет
migrateнад ней.verbosityимеет такое же поведение, как и вrun_tests().autoclobberописывает поведение, которое будет происходить, если обнаружится база данных с тем же именем, что и тестовая база данных:- Если
autoclobberравноFalse, пользователю будет предложено подтвердить уничтожение существующей базы данных.sys.exitбудет вызвано, если пользователь не одобрит. - Если autoclobber равно
True, база данных будет уничтожена без консультации с пользователем.
serializeопределяет, будет ли Django сериализовать базу данных в строку JSON в памяти перед запуском тестов (используется для восстановления состояния базы данных между тестами, если у вас нет транзакций). Вы можете установить это вFalseдля ускорения времени создания, если у вас нет ни одного класса тестов с serialized_rollback=True.Если вы используете стандартный исполняемый модуль тестов, вы можете управлять этим с помощью записи
SERIALIZEв словареTESTkeepdbопределяет, должен ли запуск тестов использовать существующую базу данных или создать новую. ЕслиTrue, будет использоваться существующая база данных, или она будет создана, если отсутствует. ЕслиFalse, будет создана новая база данных, запросив у пользователя разрешение на удаление существующей, если она имеется.Возвращает имя тестовой базы данных, которую она создала.
create_test_db()имеет побочный эффект изменения значенияNAMEвDATABASESдля соответствия имени тестовой базы данных.Аргумент
serializeбыл добавлен.Аргумент
keepdbбыл добавлен. - Если
-
destroy_test_db(old_database_name, verbosity=1, keepdb=False) -
Удаляет базу данных, имя которой является значением
NAMEвDATABASESи устанавливаетNAMEв значениеold_database_name.Аргумент
verbosityимеет такое же поведение, как и дляDiscoverRunner.Если аргумент
keepdbравенTrue, то соединение с базой данных будет закрыто, но база данных не будет удалена.Аргумент
keepdbбыл добавлен.
Интеграция с coverage.py
Покрытие кода описывает, сколько исходного кода было протестировано. Оно показывает, какие части вашего кода тестируются, а какие нет. Это важная часть тестирования приложений, поэтому рекомендуется проверять покрытие ваших тестов.
Django легко интегрируется с coverage.py, инструментом для измерения покрытия кода Python-программ. Сначала установите coverage.py. Далее выполните следующие действия из папки вашего проекта, содержащей manage.py:
coverage run --source='.' manage.py test myapp
Это выполняет ваши тесты и собирает данные о покрытии выполненных файлов в вашем проекте. Вы можете увидеть отчёт об этих данных, набрав следующую команду:
coverage report
Обратите внимание, что некоторые фрагменты кода Django были выполнены во время запуска тестов, но они не отображаются здесь из-за флага source , переданного в предыдущей команде.
Для получения дополнительных опций, таких как аннотированные HTML-списки, показывающие пропущенные строки, см. документацию coverage.py.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.8/topics/testing/advanced/