Spec-Zone.ru › Django 1.11

Дополнительные темы тестирования

Фабрика запросов

class RequestFactory [source]

Фабрика RequestFactory имеет тот же API, что и клиент тестирования. Однако вместо имитации поведения браузера, RequestFactory предоставляет способ генерации экземпляра запроса, который можно использовать в качестве первого аргумента для любого представления. Это означает, что вы можете протестировать функцию представления так же, как и любую другую функцию – как «черный ящик», с точно известными входными данными, проверяя определённые выходные данные.

API фабрики RequestFactory представляет собой слегка ограниченный подмножество API клиента тестирования:

  • Он имеет доступ только к HTTP-методам get(), post(), put(), delete(), head(), options() и trace().
  • Эти методы принимают все те же аргументы, кроме follow. Поскольку это просто фабрика для создания запросов, обработка ответа ложится на вас.
  • Он не поддерживает 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)

Тестирование и несколько имен хостов

Настройка ALLOWED_HOSTS проверяется при запуске тестов. Это позволяет клиенту тестирования различать внутренние и внешние URL-адреса.

Проекты, которые поддерживают мультитенантность или иным образом изменяют бизнес-логику в зависимости от хоста запроса и используют пользовательские имена хостов в тестах, должны включать эти хосты в ALLOWED_HOSTS.

Самый простой способ сделать это — добавить хосты в файл настроек. Например, набор тестов для docs.djangoproject.com включает следующее:

from django.test import TestCase

class SearchFormTestCase(TestCase):
    def test_empty_get(self):
        response = self.client.get('/en/dev/search/', HTTP_HOST='docs.djangoproject.dev:8000')
        self.assertEqual(response.status_code, 200)

и файл настроек включает список доменов, поддерживаемых проектом:

ALLOWED_HOSTS = [
    'www.djangoproject.dev',
    'docs.djangoproject.dev',
    ...
]

Другой вариант — добавить необходимые хосты в ALLOWED_HOSTS с помощью override_settings() или modify_settings(). Этот вариант может быть предпочтительнее в автономных приложениях, которые не могут упаковать свой собственный файл настроек, или для проектов, где список доменов не является статическим (например, поддомены для мультитенантности). Например, вы можете написать тест для домена http://otherserver/ следующим образом:

from django.test import TestCase, override_settings

class MultiDomainTestCase(TestCase):
    @override_settings(ALLOWED_HOSTS=['otherserver'])
    def test_other_domain(self):
        response = self.client.get('http://otherserver/foo/bar/')

Отключение проверки ALLOWED_HOSTS (ALLOWED_HOSTS = ['*']) при запуске тестов предотвращает вывод клиентом тестирования полезного сообщения об ошибке, если вы перенаправляетесь на внешний URL.

Изменено в Django 1.11:

Более ранние версии не проверяли ALLOWED_HOSTS во время тестирования, поэтому эти методы не были необходимы.

Тестирование и несколько баз данных

Тестирование конфигураций первичная/повторительная

Если вы тестируете конфигурацию с несколькими базами данных с репликацией первичной/повторительной (в некоторых базах данных это называется мастер/раб), данная стратегия создания тестовых баз данных создаёт проблему. Когда создаются тестовые базы данных, репликации не будет, и в результате данные, созданные на первичной базе, не будут видны на повторителе.

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

  1. Выполнение глобальной настройки до теста.
  2. Поиск тестов в любом файле ниже текущего каталога, имя которого соответствует шаблону test*.py.
  3. Создание тестовых баз данных.
  4. Запуск migrate для установки моделей и начальных данных в тестовые базы данных.
  5. Запуск проверок системы.
  6. Запуск найденных тестов.
  7. Удаление тестовых баз данных.
  8. Выполнение глобального завершения теста.
Изменено в Django 1.11:

Добавлены проверки системы.

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

Если debug_sql равно True, в случаях с ошибочными тестовыми случаями будут выведены запросы к базе данных, записанные в журнал django.db.backends, а также трассировка стека. Если verbosity равно 2, то запросы во всех тестах будут выведены.

Django может время от времени расширять возможности тестового исполнителя, добавляя новые аргументы. Объявление **kwargs позволяет это расширение. Если вы подклассируете DiscoverRunner или напишете собственный тестовый исполнитель, убедитесь, что он принимает **kwargs.

Ваш тестовый исполнитель также может определить дополнительные параметры командной строки. Создайте или переопределите метод класса add_arguments(cls, parser) и добавьте пользовательские аргументы, вызвав parser.add_argument() внутри метода, чтобы команда test смогла использовать эти аргументы.

Новое в Django 1.11:

Добавлен ключевой аргумент debug_mode.

Атрибуты

DiscoverRunner.test_suite

Класс, используемый для построения тестового набора. По умолчанию он установлен в unittest.TestSuite. Это можно переопределить, если вы хотите реализовать другую логику для сбора тестов.

DiscoverRunner.test_runner

Это класс низкоуровневого тестового исполнителя, который используется для выполнения отдельных тестов и форматирования результатов. По умолчанию он установлен в unittest.TextTestRunner. Несмотря на неудачное сходство в соглашениях об именовании, это не тот же тип класса, что и DiscoverRunner, который охватывает более широкий набор обязанностей. Вы можете переопределить этот атрибут, чтобы изменить способ запуска и отчёта о тестах.

DiscoverRunner.test_loader

Это класс, который загружает тесты, будь то из TestCases или модулей, или каким-либо другим способом, и объединяет их в наборы тестов для выполнения исполнителем. По умолчанию он установлен в unittest.defaultTestLoader. Вы можете переопределить этот атрибут, если ваши тесты будут загружаться необычными способами.

Методы

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 в значение self.debug_mode (по умолчанию 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]

Создаёт тестовые базы данных, вызывая setup_databases().

DiscoverRunner.run_checks() [source]
Новое в Django 1.11.

Выполняет проверки системы.

DiscoverRunner.run_suite(suite, **kwargs) [source]

Выполняет тестовый набор.

Возвращает результат, полученный при выполнении тестового набора.

DiscoverRunner.get_test_runner_kwargs() [source]
Новое в Django 1.11.

Возвращает аргументы ключевых слов для инициализации DiscoverRunner.test_runner.

DiscoverRunner.teardown_databases(old_config, **kwargs) [source]

Удаляет тестовые базы данных, восстанавливая условия до теста, вызывая teardown_databases().

DiscoverRunner.teardown_test_environment(**kwargs) [source]

Восстанавливает среду до начала теста.

DiscoverRunner.suite_result(suite, result, **kwargs) [source]

Вычисляет и возвращает код возврата, основанный на наборе тестов и результате этого набора тестов.

Утилиты для тестирования

django.test.utils

Для помощи в создании собственного тестового запуска Django предоставляет ряд утилит в модуле django.test.utils.

setup_test_environment(debug=None) [source]

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

Если debug не None, настройка DEBUG обновляется на её значение.

Изменено в Django 1.11:

Добавлен аргумент debug.

teardown_test_environment() [source]

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

setup_databases(verbosity, interactive, keepdb=False, debug_sql=False, parallel=0, **kwargs) [source]
Новое в Django 1.11.

Создаёт тестовые базы данных.

Возвращает структуру данных, которая предоставляет достаточно подробную информацию для отмены изменений, которые были внесены. Эта информация будет передана функции teardown_databases() по окончании тестирования.

teardown_databases(old_config, parallel=0, keepdb=False) [source]
Новое в Django 1.11.

Удаляет тестовые базы данных, восстанавливая состояние до начала тестирования.

old_config — это структура данных, определяющая изменения в конфигурации базы данных, которые необходимо отменить. Это возвращаемое значение метода setup_databases().

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 в словаре TEST.

keepdb определяет, должен ли запуск теста использовать существующую базу данных или создать новую. Если True, будет использоваться существующая база данных или создана, если её нет. Если False, будет создана новая база данных, запрашивая пользователя об удалении существующей, если она присутствует.

Возвращает имя тестовой базы данных, которое она создала.

create_test_db() имеет побочный эффект изменения значения NAME в DATABASES для соответствия имени тестовой базы данных.

destroy_test_db(old_database_name, verbosity=1, keepdb=False)

Удаляет базу данных, имя которой является значением NAME в DATABASES, и устанавливает NAME на значение old_database_name.

Аргумент verbosity имеет то же поведение, что и для DiscoverRunner.

Если аргумент keepdb равен True, то подключение к базе данных будет закрыто, но база данных не будет уничтожена.

Интеграция с 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.11/topics/testing/advanced/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API