Темы расширенного тестирования
Фабрика запросов
-
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)
Тестирование с несколькими базами данных
Тестирование конфигураций первичный/реплицирующий
Если вы тестируете конфигурацию с несколькими базами данных с репликацией первичный/реплицирующий (в некоторых базах данных она называется мастер/слейв), стратегия создания тестовых баз данных вызывает проблемы. При создании тестовых баз данных репликация не будет работать, и в результате данные, созданные на первичной базе, не будут видны на реплике.
Для компенсации этого 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] -
Выполняет завершающие действия после выполнения тестов, например, удаление магических подключений к системе шаблонов и восстановление нормальной работы почтовой службы.
Создание подключений к базе данных для тестирования
Модуль создания подключения к базе данных также предоставляет некоторые утилиты, которые могут быть полезны при тестировании.
-
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в словареTEST.keepdbопределяет, использовать ли существующую базу данных при запуске теста или создавать новую. ЕслиTrue, будет использоваться существующая база данных, или она будет создана, если её нет. ЕслиFalse, будет создана новая база данных, запросив пользователя удалить существующую, если она есть.Возвращает имя созданной тестовой базы данных.
create_test_db()имеет побочный эффект изменения значенияNAMEвDATABASESдля соответствия имени тестовой базы данных.Аргумент
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.9/topics/testing/advanced/