Инструменты тестирования
Django предоставляет небольшой набор инструментов, которые могут пригодиться при написании тестов.
Клиент для тестирования
Клиент для тестирования — это класс Python, который действует как виртуальный веб-браузер, позволяющий тестировать представления и взаимодействовать с вашим приложением Django программно.
Вот некоторые действия, которые можно выполнить с клиентом для тестирования:
- Моделирование запросов GET и POST к URL и наблюдение за ответом — от низкоуровневого HTTP (заголовки результата и коды состояния) до содержимого страницы.
- Просмотр цепочки редиректов (если таковые имеются) и проверка URL и кода состояния на каждом шаге.
- Проверка того, что заданный запрос отображается заданным шаблоном Django с контекстом шаблона, содержащим определённые значения.
Обратите внимание, что клиент для тестирования не предназначен для замены Selenium или других фреймворков «в браузере». Клиент для тестирования Django имеет другую область применения. Короче говоря:
- Используйте клиент для тестирования Django, чтобы убедиться, что отображается правильный шаблон и что шаблон получает правильные данные контекста.
- Используйте фреймворки в браузере, такие как Selenium, для проверки отображённого HTML и поведения веб-страниц, а именно функциональности JavaScript. Django также предоставляет специальную поддержку для этих фреймворков; см. раздел о
LiveServerTestCaseдля получения более подробной информации.
Комплект тестов должен использовать комбинацию обоих типов тестов.
Обзор и быстрый пример
Для использования клиента для тестирования, создайте экземпляр django.test.Client и получите веб-страницы:
>>> from django.test import Client
>>> c = Client()
>>> response = c.post('/login/', {'username': 'john', 'password': 'smith'})
>>> response.status_code
200
>>> response = c.get('/customer/details/')
>>> response.content
b'<!DOCTYPE html...'
Как показывает этот пример, вы можете создать экземпляр Client внутри сеанса интерактивного интерпретатора Python.
Обратите внимание на несколько важных моментов о работе клиента для тестирования:
- Клиенту для тестирования не требуется, чтобы веб-сервер работал. На самом деле, он будет работать без запуска веб-сервера! Это связано с тем, что он избегает накладных расходов HTTP и работает напрямую с фреймворком Django. Это помогает ускорить выполнение модульных тестов.
-
При получении страниц не забудьте указать путь к URL, а не весь домен. Например, это правильно:
>>> c.get('/login/')Это неправильно:
>>> c.get('https://www.example.com/login/')Клиент для тестирования не может получать веб-страницы, которые не работают на вашем проекте Django. Если вам нужно получить другие веб-страницы, используйте модуль стандартной библиотеки Python, например,
urllib. - Для разрешения URL-адресов клиент для тестирования использует тот URLconf, на который указывает ваше значение настройки
ROOT_URLCONF. -
Хотя приведенный выше пример будет работать в интерактивном интерпретаторе Python, некоторые функции клиента для тестирования, особенно функции, связанные с шаблонами, доступны только во время выполнения тестов.
Причина этого в том, что запуск тестов Django выполняет некоторую «магию», чтобы определить, какой шаблон был загружен данным представлением. Эта «магия» (по сути, подмена системы шаблонов Django в памяти) происходит только во время выполнения тестов.
-
По умолчанию клиент для тестирования отключит все проверки CSRF, выполняемые вашим сайтом.
Если по какой-то причине вы хотите, чтобы клиент для тестирования выполнял проверки CSRF, вы можете создать экземпляр клиента для тестирования, который выполняет проверки CSRF. Для этого передайте аргумент
enforce_csrf_checksпри создании своего клиента:>>> from django.test import Client >>> csrf_client = Client(enforce_csrf_checks=True)
Выполнение запросов
Используйте класс django.test.Client для выполнения запросов.
-
class Client(enforce_csrf_checks=False, **defaults)[source] -
Он не требует аргументов при создании. Однако вы можете использовать ключевые аргументы для указания некоторых значений по умолчанию для заголовков. Например, это отправит
User-AgentHTTP-заголовок в каждом запросе:>>> c = Client(HTTP_USER_AGENT='Mozilla/5.0')
Значения из ключевых аргументов
extra, переданных вget(),post()и т.д., имеют приоритет над значениями по умолчанию, переданными в конструктор класса.Аргумент
enforce_csrf_checksможет использоваться для тестирования защиты от CSRF (см. выше).После получения экземпляра
Clientвы можете вызвать любой из следующих методов:-
get(path, data=None, follow=False, secure=False, **extra)[source] -
Отправляет запрос GET по предоставленному
pathи возвращает объектResponse, который документирован ниже.Ключевые пары в словаре
dataиспользуются для создания полезной нагрузки данных GET. Например:>>> c = Client() >>> c.get('/customers/details/', {'name': 'fred', 'age': 7})…приведет к выполнению запроса GET, эквивалентного:
/customers/details/?name=fred&age=7
Ключевые аргументы
extraмогут использоваться для указания заголовков, которые необходимо отправить в запросе. Например:>>> c = Client() >>> c.get('/customers/details/', {'name': 'fred', 'age': 7}, ... HTTP_X_REQUESTED_WITH='XMLHttpRequest')…отправит HTTP-заголовок
HTTP_X_REQUESTED_WITHв представление деталей, что является хорошим способом проверки путей кода, использующих методdjango.http.HttpRequest.is_ajax().Спецификация CGI
Заголовки, отправленные через
**extra, должны соответствовать спецификации CGI. Например, эмуляция другого заголовка «Host», отправленного в HTTP-запросе от браузера к серверу, должна быть передана какHTTP_HOST.Если у вас уже есть аргументы GET в кодированном URL-формате, вы можете использовать эту кодировку вместо аргумента data. Например, предыдущий запрос GET также можно отправить как:
>>> c = Client() >>> c.get('/customers/details/?name=fred&age=7')Если вы предоставляете URL с закодированными данными GET и аргументом data, аргумент data будет иметь приоритет.
Если вы установите
followвTrue, клиент будет следовать всем перенаправлениям, и в объекте ответа будет задано атрибутredirect_chain, содержащий кортежи промежуточных URL-адресов и кодов состояния.Если у вас был URL
/redirect_me/, который перенаправлял на/next/, который перенаправлял на/final/, вот что вы увидите:>>> response = c.get('/redirect_me/', follow=True) >>> response.redirect_chain [('http://testserver/next/', 302), ('http://testserver/final/', 302)]Если вы установите
secureвTrue, клиент будет эмулировать HTTPS-запрос.
-
post(path, data=None, content_type=MULTIPART_CONTENT, follow=False, secure=False, **extra)[source] -
Отправляет запрос POST по предоставленному
pathи возвращает объектResponse, который документирован ниже.Ключевые пары в словаре
dataиспользуются для отправки данных POST. Например:>>> c = Client() >>> c.post('/login/', {'name': 'fred', 'passwd': 'secret'})…приведет к выполнению запроса POST по этому URL:
/login/
…с этими данными POST:
name=fred&passwd=secret
Если вы предоставите
content_type(например, text/xml для XML-полезной нагрузки), содержимоеdataбудет отправлено как есть в запросе POST, используяcontent_typeв HTTP-заголовкеContent-Type.Если вы не укажете значение для
content_type, значения вdataбудут переданы с типом контента multipart/form-data. В этом случае ключевые пары вdataбудут закодированы как multipart-сообщение и использованы для создания полезной нагрузки данных POST.Для отправки нескольких значений для заданного ключа (например, для указания выбранных значений для
<select multiple>) укажите значения как список или кортеж для требуемого ключа. Например, это значениеdataотправит три выбранных значения для поля с именемchoices:{'choices': ('a', 'b', 'd')}Отправка файлов является особым случаем. Для отправки файла вам нужно указать только имя поля файла в качестве ключа и дескриптор файла файла, который вы хотите загрузить, в качестве значения. Например:
>>> c = Client() >>> with open('wishlist.doc') as fp: ... c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp})(Имя
attachmentздесь не имеет значения; используйте любое имя, которое ожидает ваш код обработки файлов.)Вы также можете предоставить любой объект, подобный файлу (например,
StringIOилиBytesIO) как дескриптор файла.Возможность использования объекта, подобного файлу, была добавлена.
Обратите внимание, что если вы хотите использовать тот же дескриптор файла для нескольких вызовов
post(), вам потребуется вручную сбросить указатель файла между отправками. Самый простой способ сделать это — вручную закрыть файл после того, как он был предоставленpost(), как показано выше.Вы также должны убедиться, что файл открыт таким образом, чтобы данные могли быть прочитаны. Если ваш файл содержит двоичные данные, такие как изображение, это означает, что вам нужно открыть файл в режиме
rb(чтение в двоичном формате).Аргумент
extraдействует аналогично дляClient.get().Если URL, который вы запрашиваете с помощью POST, содержит закодированные параметры, эти параметры будут доступны в данных request.GET. Например, если вы отправите запрос:
>>> c.post('/login/?visitor=true', {'name': 'fred', 'passwd': 'secret'})…представление, обрабатывающее этот запрос, может запросить request.POST для извлечения имени пользователя и пароля и может запросить request.GET, чтобы определить, был ли пользователь посетителем.
Если вы установите
followвTrue, клиент будет следовать всем перенаправлениям, и в объекте ответа будет задано атрибутredirect_chain, содержащий кортежи промежуточных URL-адресов и кодов состояния.Если вы установите
secureвTrue, клиент будет эмулировать HTTPS-запрос.
-
head(path, data=None, follow=False, secure=False, **extra)[source] -
Отправляет запрос HEAD по предоставленному
pathи возвращает объектResponse. Этот метод работает так же, какClient.get(), включая аргументыfollow,secureиextra, за исключением того, что он не возвращает тело сообщения.
-
options(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)[source] -
Отправляет запрос OPTIONS по предоставленному
pathи возвращает объектResponse. Полезно для тестирования RESTful-интерфейсов.Если
dataпредоставлено, оно используется в качестве тела запроса, а заголовокContent-Typeустанавливается в значениеcontent_type.Аргументы
follow,secureиextraдействуют аналогично дляClient.get().
-
put(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)[source] -
Отправляет запрос PUT по предоставленному
pathи возвращает объектResponse. Полезно для тестирования RESTful-интерфейсов.Если
dataпредоставлено, оно используется в качестве тела запроса, а заголовокContent-Typeустанавливается в значениеcontent_type.Аргументы
follow,secureиextraдействуют аналогично дляClient.get().
-
patch(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)[source] -
Отправляет запрос PATCH по предоставленному
pathи возвращает объектResponse. Полезно для тестирования RESTful-интерфейсов.Аргументы
follow,secureиextraдействуют аналогично дляClient.get().
-
delete(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)[source] -
Отправляет запрос DELETE по предоставленному
pathи возвращает объектResponse. Полезно для тестирования RESTful-интерфейсов.Если
dataпредоставлено, оно используется в качестве тела запроса, а заголовокContent-Typeустанавливается в значениеcontent_type.Аргументы
follow,secureиextraдействуют аналогично дляClient.get().
-
-
trace(path, follow=False, secure=False, **extra)[source] -
Выполняет запрос TRACE по предоставленному
pathи возвращает объектResponse. Полезно для имитации диагностических зондирований.В отличие от других методов запроса,
dataне предоставляется в качестве параметра ключевого слова, чтобы соответствовать RFC 7231#section-4.3.8, который предписывает, что запросы TRACE не должны иметь тела.Аргументы
follow,secure, иextraдействуют так же, как и дляClient.get().
-
login(**credentials)[source] -
Если ваш сайт использует систему аутентификации Django и вы работаете с входом пользователей, вы можете использовать метод
login()тестового клиента для имитации входа пользователя на сайт.Неактивные пользователи (
is_active=False) не допускаются к входу, так как этот метод предназначен для эквивалентности представлениюlogin(), которое используетAuthenticationFormи поэтому по умолчанию отклоняет неактивных пользователей.После вызова этого метода, тестовый клиент будет иметь все необходимые куки и данные сессии для прохождения любых основанных на входе тестов, которые могут быть частью представления.
Формат аргумента
credentialsзависит от используемого бекенда аутентификации (который настраивается вашим параметромAUTHENTICATION_BACKENDS). Если вы используете стандартный бекенд аутентификации Django (ModelBackend),credentialsдолжны быть имя пользователя и пароль пользователя, предоставленные в качестве ключевых аргументов:>>> c = Client() >>> c.login(username='fred', password='secret') # Now you can access a view that's only available to logged-in users.
Если вы используете другой бекенд аутентификации, этот метод может потребовать другие данные авторизации. Он требует любых данных авторизации, необходимых методом
authenticate()вашего бекенда.login()возвращаетTrueесли данные авторизации были приняты и вход выполнен успешно.Наконец, вам нужно будет создать учетные записи пользователей, прежде чем вы сможете использовать этот метод. Как мы объяснили выше, тестовый запуск выполняется с использованием тестовой базы данных, которая по умолчанию не содержит пользователей. В результате учетные записи пользователей, которые действуют на вашем сайте производства, не будут работать в тестовых условиях. Вам нужно будет создать пользователей в рамках набора тестов – либо вручную (используя API модели Django), либо с помощью тестового фикстура. Помните, что если вы хотите, чтобы ваш тестовый пользователь имел пароль, вы не можете установить пароль пользователя, установив атрибут password напрямую – вы должны использовать функцию
set_password()для хранения правильно хэшированного пароля. В качестве альтернативы, вы можете использовать вспомогательный методcreate_user()для создания нового пользователя с правильно хэшированным паролем.
-
force_login(user, backend=None)[source] -
Если ваш сайт использует систему аутентификации Django, вы можете использовать метод
force_login()для имитации входа пользователя на сайт. Используйте этот метод вместоlogin(), когда тест требует входа пользователя, и детали того, как пользователь вошёл, не важны.В отличие от
login(), этот метод пропускает шаги аутентификации и проверки: неактивные пользователи (is_active=False) допускаются к входу, и данные авторизации пользователя не нужно предоставлять.У пользователя будет установлен атрибут
backendв значение аргументаbackend(который должен быть строкой с точечной записью Python), или вsettings.AUTHENTICATION_BACKENDS[0]если значение не указано. Функцияauthenticate(), вызываемаяlogin(), обычно аннотирует пользователя таким образом.Этот метод быстрее, чем
login(), так как дорогостоящие алгоритмы хэширования паролей пропускаются. Кроме того, вы можете ускоритьlogin()с помощью использования более слабого хэширования при тестировании.
-
logout()[source] -
Если ваш сайт использует систему аутентификации Django, метод
logout()может быть использован для имитации выхода пользователя из вашего сайта.После вызова этого метода, тестовый клиент очистит все куки и данные сессии по умолчанию. Следующие запросы будут казаться исходящими от
AnonymousUser.
-
Тестирование ответов
Методы get() и post() оба возвращают объект Response. Этот объект Response не эквивалентен объекту HttpResponse, возвращаемому представлениями Django; объект тестового ответа содержит некоторые дополнительные данные, полезные для кода теста для проверки.
В частности, объект Response имеет следующие атрибуты:
-
class Response -
-
client -
Тестовый клиент, который использовался для отправки запроса, приведшего к получению ответа.
-
content -
Тело ответа в виде байтовой строки. Это конечное содержимое страницы, как оно было представлено представлением, или любое сообщение об ошибке.
-
context -
Экземпляр шаблона
Context, который использовался для рендеринга шаблона, сгенерировавшего содержимое ответа.Если для рендеринга страницы использовалось несколько шаблонов, то
contextбудет списком объектовContext, в порядке их рендеринга.Независимо от количества шаблонов, используемых во время рендеринга, вы можете получить значения контекста, используя оператор
[]. Например, переменную контекстаnameможно получить так:>>> response = client.get('/foo/') >>> response.context['name'] 'Arthur'Не используете Django шаблоны?
Это атрибут заполняется только при использовании бэкэнда
DjangoTemplates. Если вы используете другой движок шаблонов,context_dataможет быть подходящей альтернативой для ответов с этим атрибутом.
-
json(**kwargs) -
Тело ответа, разобранное как JSON. Дополнительные ключевые аргументы передаются в
json.loads(). Например:>>> response = client.get('/foo/') >>> response.json()['name'] 'Arthur'Если заголовок
Content-Typeне"application/json", при попытке разбора ответа будет поднята ошибкаValueError.
-
request -
Данные запроса, которые вызвали ответ.
-
wsgi_request -
Экземпляр
WSGIRequest, сгенерированный обработчиком теста, который сгенерировал ответ.
-
status_code -
HTTP статус ответа в виде целого числа. Полный список кодов см. в реестре кодов состояния IANA.
-
templates -
Список экземпляров
Template, используемых для рендеринга конечного содержимого, в порядке их рендеринга. Для каждого шаблона в списке используйтеtemplate.nameдля получения имени файла шаблона, если шаблон был загружен из файла. (Имя – это строка, например,'admin/index.html'.)Не используете Django шаблоны?
Этот атрибут заполняется только при использовании бэкэнда
DjangoTemplates. Если вы используете другой движок шаблонов,template_nameможет быть подходящей альтернативой, если вам нужно только имя шаблона, использованного для рендеринга.
-
resolver_match -
Экземпляр
ResolverMatchдля ответа. Вы можете использовать атрибутfunc, например, для проверки представления, обслужившего ответ:# my_view here is a function based view self.assertEqual(response.resolver_match.func, my_view) # class-based views need to be compared by name, as the functions # generated by as_view() won't be equal self.assertEqual(response.resolver_match.func.__name__, MyView.as_view().__name__)
Если заданный URL не найден, доступ к этому атрибуту вызовет исключение
Resolver404.
-
Вы также можете использовать синтаксис словаря для объекта ответа, чтобы получить значение любого параметра в заголовках HTTP. Например, вы можете определить тип содержимого ответа, используя response['Content-Type'].
Исключения
Если вы направите тестовый клиент на представление, которое вызывает исключение, это исключение будет видно в тестовом случае. Затем вы можете использовать стандартный блок try ... except или assertRaises() для проверки исключений.
Единственные исключения, которые не видны тестовому клиенту, это Http404, PermissionDenied, SystemExit и SuspiciousOperation. Django обрабатывает эти исключения внутри и преобразует их в соответствующие коды HTTP-ответа. В этих случаях вы можете проверить response.status_code в вашем тесте.
Состояние
Тестовый клиент имеет состояние. Если ответ возвращает cookie, то эта cookie будет сохранена в тестовом клиенте и отправлена со всеми последующими get() и post() запросами.
Политики истечения срока действия этих cookie не соблюдаются. Если вы хотите, чтобы cookie истекли, удалите их вручную или создайте новый экземпляр Client (что фактически удалит все cookie).
Тестовый клиент имеет два атрибута, которые хранят информацию о состоянии. Вы можете получить доступ к этим свойствам как часть условия теста.
-
Объект Python
SimpleCookie, содержащий текущие значения всех cookie клиента. Для получения дополнительной информации см. документацию модуляhttp.cookies.
-
Client.session -
Объект, похожий на словарь, содержащий сессионную информацию. Полные подробности см. в документации по сессиям.
Чтобы изменить сессию и сохранить ее, необходимо сначала сохранить ее в переменной (потому что каждый раз при обращении к этому свойству создается новая
SessionStore):def test_something(self): session = self.client.session session['somekey'] = 'test' session.save()
Пример
Ниже приведен простой тестовый пример с использованием тестового клиента:
import unittest
from django.test import Client
class SimpleTest(unittest.TestCase):
def setUp(self):
# Every test needs a client.
self.client = Client()
def test_details(self):
# Issue a GET request.
response = self.client.get('/customer/details/')
# Check that the response is 200 OK.
self.assertEqual(response.status_code, 200)
# Check that the rendered context contains 5 customers.
self.assertEqual(len(response.context['customers']), 5)
См. также
Предоставленные классы тестовых случаев
Обычные классы тестов Python наследуются от базового класса unittest.TestCase. Django предоставляет несколько расширений этого базового класса:
Преобразование обычного класса unittest.TestCase в любой из подклассов простое: измените базовый класс своего теста с unittest.TestCase на подкласс. Все стандартные функции тестирования Python будут доступны, и они будут дополнены полезными дополнениями, как описано в каждом разделе ниже.
SimpleTestCase
-
class SimpleTestCase[source]
Подкласс unittest.TestCase, который добавляет эту функциональность:
- Некоторые полезные утверждения, такие как:
- Проверка вызова
raises a certain exception. - Тестирование поля формы
rendering and error treatment. - Тестирование
HTML responses for the presence/lack of a given fragment. - Проверка использования шаблона
has/hasn't been used to generate a given response content. - Проверка того, что приложение выполняет HTTP
redirect. - Надежное тестирование двух
HTML fragmentsна равенство/неравенство илиcontainment. - Надежное тестирование двух
XML fragmentsна равенство/неравенство. - Надежное тестирование двух
JSON fragmentsна равенство.
- Проверка вызова
- Возможность запуска тестов с изменёнными настройками.
- Использование
clientClient. - Настройка
URL mapsво время тестирования.
Если ваши тесты обращаются к базе данных, используйте подклассы TransactionTestCase или TestCase.
-
SimpleTestCase.allow_database_queries -
SimpleTestCaseпо умолчанию запрещает запросы к базе данных. Это помогает избежать выполнения записывающих запросов, которые могут повлиять на другие тесты, так как каждыйSimpleTestCaseтест не выполняется в транзакции. Если вы не беспокоитесь об этой проблеме, вы можете отключить это поведение, установив атрибут классаallow_database_queriesвTrueв вашем тестовом классе.
Предупреждение
SimpleTestCase и его подклассы (например, TestCase, ...) полагаются на setUpClass() и tearDownClass() для выполнения некоторых инициализаций на уровне класса (например, переопределение настроек). Если вам нужно переопределить эти методы, не забудьте вызвать реализацию super:
class MyTestCase(TestCase):
@classmethod
def setUpClass(cls):
super(MyTestCase, cls).setUpClass()
...
@classmethod
def tearDownClass(cls):
...
super(MyTestCase, cls).tearDownClass()
Учитывайте поведение Python, если во время setUpClass() возникает исключение. В этом случае ни тесты в классе, ни tearDownClass() не выполняются. В случае django.test.TestCase это приведёт к утечке транзакции, созданной в super(), что приводит к различным симптомам, включая ошибку сегментации на некоторых платформах (сообщалось об OS X). Если вы хотите намеренно вызвать исключение, например, unittest.SkipTest в setUpClass(), убедитесь, что вы делаете это до вызова super(), чтобы избежать этого.
TransactionTestCase
-
class TransactionTestCase[source]
TransactionTestCase наследуется от SimpleTestCase, чтобы добавить некоторые функции, специфичные для базы данных:
- Сброс базы данных до известного состояния в начале каждого теста для облегчения тестирования и использования ORM.
- Фикстуры базы данных
fixtures. - Пропуск тестов в зависимости от особенностей бэкенда базы данных пропуска тестов.
- Остальные специализированные методы
assert*.
Класс TestCase Django — более часто используемый подкласс TransactionTestCase, который использует механизмы транзакций базы данных для ускорения сброса базы данных до известного состояния в начале каждого теста. Однако следствием этого является то, что некоторые поведения базы данных нельзя проверить в классе Django TestCase. Например, вы не можете проверить, что блок кода выполняется в транзакции, как требуется при использовании select_for_update(). В этих случаях следует использовать TransactionTestCase.
В более старых версиях Django эффекты коммита и отката транзакции нельзя было проверить в TestCase. С завершением цикла устаревания старой системы управления транзакциями в Django 1.8 команды управления транзакциями (например, transaction.commit()) больше не отключаются в TestCase.
TransactionTestCase и TestCase идентичны, за исключением способа сброса базы данных до известного состояния и возможности для кода тестов проверить эффекты коммита и отката:
TransactionTestCaseсбрасывает базу данных после выполнения теста, обнуляя все таблицы.TransactionTestCaseможет вызвать commit и rollback и наблюдать за эффектами этих вызовов на базе данных.TestCase, с другой стороны, не обнуляет таблицы после теста. Вместо этого он заключает код теста в транзакцию базы данных, которая отменяется в конце теста. Это гарантирует, что откат в конце теста восстановит базу данных в исходное состояние.
Предупреждение
TestCase при запуске на базе данных, которая не поддерживает откат (например, MySQL с движком хранения MyISAM), и все экземпляры TransactionTestCase откатит в конце теста, удалив все данные из тестовой базы данных.
Приложения не увидят свои данные, загруженные повторно; если вам нужна эта функциональность (например, сторонние приложения должны её включить), вы можете установить serialized_rollback = True внутри тела TestCase.
TestCase
-
class TestCase[source]
Это наиболее распространённый класс для написания тестов в Django. Он наследуется от TransactionTestCase (и, следовательно, SimpleTestCase). Если ваше приложение Django не использует базу данных, используйте SimpleTestCase.
Класс:
- Оборачивает тесты в два вложенных
atomic()блока: один для всего класса и один для каждого теста. Поэтому, если вы хотите проверить определённое поведение транзакции базы данных, используйтеTransactionTestCase.
Он также предоставляет дополнительный метод:
-
classmethod TestCase.setUpTestData()[source] -
Указанный выше блок уровня класса
atomicпозволяет создавать начальные данные на уровне класса один раз для всегоTestCase. Это позволяет ускорить тесты по сравнению с использованиемsetUp().Например:
from django.test import TestCase class MyTests(TestCase): @classmethod def setUpTestData(cls): # Set up data for the whole TestCase cls.foo = Foo.objects.create(bar="Test") ... def test1(self): # Some test using self.foo ... def test2(self): # Some other test using self.foo ...Обратите внимание, что если тесты выполняются на базе данных без поддержки транзакций (например, MySQL с движком MyISAM),
setUpTestData()будет вызываться перед каждым тестом, что сведёт на нет преимущества скорости.Будьте осторожны, не изменяйте объекты, созданные в
setUpTestData()в ваших методах тестов. Изменения во внутренних объектах из настройки, выполненной на уровне класса, будут сохраняться между методами тестов. Если вам нужно их изменить, вы можете перезагрузить их в методеsetUp()с помощьюrefresh_from_db(), например.
LiveServerTestCase
-
class LiveServerTestCase[source]
LiveServerTestCase выполняет в основном то же самое, что и TransactionTestCase, с одной дополнительной функцией: запускает активный сервер Django в фоновом режиме при настройке и выключает его при завершении. Это позволяет использовать автоматические тестовые клиенты, отличные от Django-клиента-заглушки, такие как, например, Selenium-клиент, для выполнения ряда функциональных тестов в браузере и моделирования действий реального пользователя.
По умолчанию активный сервер прослушивает localhost и выбирает первый доступный порт в диапазоне 8081-8179. Полный URL можно получить с помощью self.live_server_url во время тестов.
В более ранних версиях, адрес активного сервера по умолчанию был всегда 'localhost:8081'.
Если вы хотите выбрать другой адрес, вы можете указать его с помощью опции test --liveserver, например:
$ ./manage.py test --liveserver=localhost:8082
В старых версиях live_server_url был доступен только из экземпляра. Теперь это свойство класса и может быть обращено из методов класса, таких как setUpClass().
Другой способ изменения адреса сервера по умолчанию — установить переменную среды DJANGO_LIVE_TEST_SERVER_ADDRESS где-то в вашем коде (например, в пользовательском тестовом загрузчике):
import os os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = 'localhost:8082'
В случае, когда тесты выполняются несколькими процессами параллельно (например, в контексте нескольких одновременных непрерывных интеграционных сборках), процессы будут конкурировать за один и тот же адрес, и поэтому ваши тесты могут случайным образом завершаться с ошибкой «Адрес уже используется». Чтобы избежать этой проблемы, вы можете передать список портов или диапазонов портов (по крайней мере, столько, сколько потенциальных параллельных процессов). Например:
$ ./manage.py test --liveserver=localhost:8082,8090-8100,9000-9200,7041
Затем, во время выполнения тестов, каждый новый активный тестовый сервер будет проверять каждый указанный порт, пока не найдёт свободный и его не займет.
Чтобы продемонстрировать, как использовать LiveServerTestCase, давайте напишем простой Selenium-тест. Прежде всего, вам необходимо установить selenium-пакет в вашу Python-среду:
$ pip install selenium
Затем добавьте LiveServerTestCase-основанный тест в модуль тестов вашего приложения (например: myapp/tests.py). Для этого примера мы будем предполагать, что вы используете приложение staticfiles и хотите, чтобы статические файлы обслуживались во время выполнения тестов так же, как мы получаем их во время разработки с DEBUG=True, т.е. без необходимости собирать их с помощью collectstatic. Мы будем использовать подкласс StaticLiveServerTestCase, который предоставляет эту функциональность. Замените его на django.test.LiveServerTestCase если вам это не нужно.
Код этого теста может выглядеть следующим образом:
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from selenium.webdriver.firefox.webdriver import WebDriver
class MySeleniumTests(StaticLiveServerTestCase):
fixtures = ['user-data.json']
@classmethod
def setUpClass(cls):
super(MySeleniumTests, cls).setUpClass()
cls.selenium = WebDriver()
@classmethod
def tearDownClass(cls):
cls.selenium.quit()
super(MySeleniumTests, cls).tearDownClass()
def test_login(self):
self.selenium.get('%s%s' % (self.live_server_url, '/login/'))
username_input = self.selenium.find_element_by_name("username")
username_input.send_keys('myuser')
password_input = self.selenium.find_element_by_name("password")
password_input.send_keys('secret')
self.selenium.find_element_by_xpath('//input[@value="Log in"]').click()
Наконец, вы можете запустить тест следующим образом:
$ ./manage.py test myapp.tests.MySeleniumTests.test_login
Этот пример автоматически откроет Firefox, затем перейдёт на страницу входа, введёт данные и нажмёт кнопку «Войти». Selenium предлагает другие драйверы на случай, если у вас нет Firefox или вы хотите использовать другой браузер. Приведённый выше пример — лишь малая часть того, что может делать Selenium-клиент; обратитесь к полной справке для получения дополнительной информации.
Примечание
При использовании в памяти базы данных SQLite для запуска тестов, одно и то же подключение к базе данных будет использоваться двумя потоками параллельно: потоком, в котором запущен активный сервер, и потоком, в котором выполняется тестовый случай. Важно предотвратить одновременные запросы к базе данных через это общее подключение двумя потоками, поскольку это иногда может случайным образом привести к сбою тестов. Таким образом, вам необходимо гарантировать, что два потока не обращаются к базе данных одновременно. В частности, это означает, что в некоторых случаях (например, сразу после нажатия ссылки или отправки формы) вам может потребоваться проверить, получен ли ответ Selenium и загружена ли следующая страница, прежде чем продолжать выполнение дальнейших тестов. Сделайте это, например, заставив Selenium дождаться, пока тег <body> не будет найден в ответе (требуется Selenium > 2.13):
def test_login(self):
from selenium.webdriver.support.wait import WebDriverWait
timeout = 2
...
self.selenium.find_element_by_xpath('//input[@value="Log in"]').click()
# Wait until the response is received
WebDriverWait(self.selenium, timeout).until(
lambda driver: driver.find_element_by_tag_name('body'))
Трудность здесь заключается в том, что на самом деле нет такого понятия, как «загрузка страницы», особенно в современных веб-приложениях, которые генерируют HTML динамически после того, как сервер сгенерировал начальный документ. Поэтому простое проверка наличия <body> в ответе может быть не совсем подходящим для всех случаев использования. Обратитесь к часто задаваемым вопросам Selenium и документации Selenium для получения дополнительной информации.
Функции тестовых случаев
Клиент по умолчанию
-
SimpleTestCase.client
Каждый тестовый случай в экземпляре django.test.*TestCase имеет доступ к экземпляру Django-клиента тестирования. К этому клиенту можно обратиться как к self.client. Этот клиент пересоздаётся для каждого теста, поэтому вам не нужно беспокоиться о сохранении состояния (например, cookies) от одного теста к другому.
Это означает, что вместо создания экземпляра Client в каждом тесте:
import unittest
from django.test import Client
class SimpleTest(unittest.TestCase):
def test_details(self):
client = Client()
response = client.get('/customer/details/')
self.assertEqual(response.status_code, 200)
def test_index(self):
client = Client()
response = client.get('/customer/index/')
self.assertEqual(response.status_code, 200)
...вы можете просто обратиться к self.client, как показано ниже:
from django.test import TestCase
class SimpleTest(TestCase):
def test_details(self):
response = self.client.get('/customer/details/')
self.assertEqual(response.status_code, 200)
def test_index(self):
response = self.client.get('/customer/index/')
self.assertEqual(response.status_code, 200)
Настройка клиента тестирования
-
SimpleTestCase.client_class
Если вы хотите использовать другой класс Client (например, подкласс с настраиваемым поведением), используйте атрибут класса client_class:
from django.test import TestCase, Client
class MyTestClient(Client):
# Specialized methods for your environment
...
class MyTest(TestCase):
client_class = MyTestClient
def test_my_stuff(self):
# Here self.client is an instance of MyTestClient...
call_some_test_code()
Загрузка фикстур
-
TransactionTestCase.fixtures
Тестовый случай для веб-сайта с поддержкой базы данных мало пригоден, если в базе данных нет данных. Тесты более читаемы и проще в обслуживании, если создавать объекты с использованием ORM, например, в TestCase.setUpTestData(), однако, вы также можете использовать фикстуры.
Фикстура — это набор данных, который Django знает как импортировать в базу данных. Например, если ваш сайт имеет учётные записи пользователей, вы можете создать фикстуру поддельных учётных записей пользователей, чтобы заполнить вашу базу данных во время тестов.
Самый простой способ создания фикстуры — использовать команду manage.py dumpdata. Это предполагает, что у вас уже есть данные в базе данных. Смотрите dumpdata
documentation для получения дополнительной информации.
После создания фикстуры и размещения её в каталоге fixtures в одном из ваших INSTALLED_APPS, вы можете использовать её в своих модульных тестах, указав атрибут класса fixtures для вашего подкласса django.test.TestCase:
from django.test import TestCase
from myapp.models import Animal
class AnimalTestCase(TestCase):
fixtures = ['mammals.json', 'birds']
def setUp(self):
# Test definitions as before.
call_setup_methods()
def testFluffyAnimals(self):
# A test that uses the fixtures.
call_some_test_code()
Вот что произойдёт конкретно:
- В начале каждого теста, до выполнения
setUp(), Django очистит базу данных, вернув базу данных в состояние, в котором она находилась непосредственно после вызоваmigrate. - Затем устанавливаются все именованные фикстуры. В этом примере Django установит все JSON-фикстуры с именем
mammals, а затем все фикстуры с именемbirds. Смотритеloaddataдля получения дополнительной информации об определении и установке фикстур.
По соображениям производительности, TestCase загружает фикстуры один раз для всего тестового класса, до выполнения setUpTestData(), а не перед каждым тестом, и использует транзакции для очистки базы данных перед каждым тестом. В любом случае, вы можете быть уверены, что результат теста не повлияет на другой тест или порядок выполнения тестов.
По умолчанию фикстуры загружаются только в базу данных default. Если вы используете несколько баз данных и установили multi_db=True, фикстуры будут загружены во все базы данных.
Настройка URLconf
-
SimpleTestCase.urls
Устарело начиная с версии 1.8: Используйте @override_settings(ROOT_URLCONF=...) для настройки URLconf вместо этого.
Если ваше приложение предоставляет представления, вы можете захотеть включить тесты, которые используют тестовый клиент для проверки этих представлений. Однако конечный пользователь свободен развертывать представления вашего приложения на любом URL-адресе по своему выбору. Это означает, что ваши тесты не могут полагаться на то, что ваши представления будут доступны по определённому URL-адресу.
Для предоставления надёжного пространства URL для ваших тестов, классы django.test.*TestCase предоставляют возможность настройки конфигурации URLconf на время выполнения набора тестов. Если ваш экземпляр *TestCase определяет атрибут urls, *TestCase будет использовать значение этого атрибута в качестве ROOT_URLCONF на время выполнения этого теста.
Например:
from django.test import TestCase
class TestMyViews(TestCase):
urls = 'myapp.test_urls'
def test_index_page_view(self):
# Here you'd test your view using ``Client``.
call_some_test_code()
В данном тестовом случае содержимое myapp.test_urls будет использоваться в качестве URLconf на протяжении всего тестового случая.
Поддержка нескольких баз данных
-
TransactionTestCase.multi_db
Django настраивает тестовую базу данных, соответствующую каждой базе данных, определённой в DATABASES определении в вашем файле настроек. Однако большая часть времени, затрачиваемого на выполнение Django TestCase, расходуется на вызов flush, гарантирующий наличие чистой базы данных в начале каждого тестового запуска. Если у вас несколько баз данных, требуется несколько операций очистки (по одной для каждой базы данных), что может быть длительной задачей, особенно если ваши тесты не требуют проверки работы с несколькими базами данных.
В качестве оптимизации Django очищает только базу данных default в начале каждого тестового запуска. Если ваша настройка содержит несколько баз данных, и у вас есть тест, который требует очистки каждой базы данных, вы можете использовать атрибут multi_db в наборе тестов, чтобы запросить полную очистку.
Например:
class TestMyViews(TestCase):
multi_db = True
def test_index_page_view(self):
call_some_test_code()
Этот тестовый случай очистит все тестовые базы данных перед запуском test_index_page_view.
Флаг multi_db также влияет на то, в какие базы данных загружаются attr:TransactionTestCase.fixtures. По умолчанию (когда multi_db=False), фикстуры загружаются только в базу данных default. Если multi_db=True, фикстуры загружаются во все базы данных.
Переопределение настроек
Предупреждение
Используйте указанные ниже функции для временного изменения значений настроек в тестах. Не изменяйте django.conf.settings напрямую, так как Django не восстановит исходные значения после таких изменений.
-
SimpleTestCase.settings()[source]
Для целей тестирования часто бывает полезно временно изменить настройку и вернуться к исходному значению после выполнения тестового кода. Для этого случая Django предоставляет стандартный менеджер контекста Python (см. PEP 343) под названием settings(), который можно использовать следующим образом:
from django.test import TestCase
class LoginTestCase(TestCase):
def test_login(self):
# First check for the default behavior
response = self.client.get('/sekrit/')
self.assertRedirects(response, '/accounts/login/?next=/sekrit/')
# Then override the LOGIN_URL setting
with self.settings(LOGIN_URL='/other/login/'):
response = self.client.get('/sekrit/')
self.assertRedirects(response, '/other/login/?next=/sekrit/')
В этом примере будет переопределена настройка LOGIN_URL для кода в блоке with и после этого её значение будет восстановлено до предыдущего состояния.
-
SimpleTestCase.modify_settings()[source]
Переопределение настроек, содержащих список значений, может оказаться неудобным. На практике часто достаточно добавления или удаления значений. Менеджер контекста modify_settings() упрощает эту задачу:
from django.test import TestCase
class MiddlewareTestCase(TestCase):
def test_cache_middleware(self):
with self.modify_settings(MIDDLEWARE_CLASSES={
'append': 'django.middleware.cache.FetchFromCacheMiddleware',
'prepend': 'django.middleware.cache.UpdateCacheMiddleware',
'remove': [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
],
}):
response = self.client.get('/')
# ...
Для каждого действия можно указать либо список значений, либо строку. Когда значение уже существует в списке, append и prepend не оказывают никакого влияния; также remove не оказывает влияния, если значение не существует.
-
override_settings()[source]
В случае, если вы хотите переопределить настройку для тестового метода, Django предоставляет декоратор override_settings() (см. PEP 318). Он используется следующим образом:
from django.test import TestCase, override_settings
class LoginTestCase(TestCase):
@override_settings(LOGIN_URL='/other/login/')
def test_login(self):
response = self.client.get('/sekrit/')
self.assertRedirects(response, '/other/login/?next=/sekrit/')
Декоратор также может быть применён к классам TestCase:
from django.test import TestCase, override_settings
@override_settings(LOGIN_URL='/other/login/')
class LoginTestCase(TestCase):
def test_login(self):
response = self.client.get('/sekrit/')
self.assertRedirects(response, '/other/login/?next=/sekrit/')
-
modify_settings()[source]
Аналогично, Django предоставляет декоратор modify_settings():
from django.test import TestCase, modify_settings
class MiddlewareTestCase(TestCase):
@modify_settings(MIDDLEWARE_CLASSES={
'append': 'django.middleware.cache.FetchFromCacheMiddleware',
'prepend': 'django.middleware.cache.UpdateCacheMiddleware',
})
def test_cache_middleware(self):
response = self.client.get('/')
# ...
Декоратор также может быть применён к классам тестовых случаев:
from django.test import TestCase, modify_settings
@modify_settings(MIDDLEWARE_CLASSES={
'append': 'django.middleware.cache.FetchFromCacheMiddleware',
'prepend': 'django.middleware.cache.UpdateCacheMiddleware',
})
class MiddlewareTestCase(TestCase):
def test_cache_middleware(self):
response = self.client.get('/')
# ...
Примечание
При применении к классу эти декораторы изменяют класс напрямую и возвращают его; они не создают и не возвращают изменённую копию. Поэтому, если вы попытаетесь изменить вышеприведённые примеры, чтобы назначить возвращаемое значение другому имени, вместо LoginTestCase или MiddlewareTestCase, вы можете быть удивлены, обнаружив, что исходные классы тестовых случаев всё равно подвергаются влиянию декоратора. Для данного класса modify_settings() всегда применяется после override_settings().
Предупреждение
Файл настроек содержит некоторые настройки, которые используются только во время инициализации внутренних компонентов Django. Если вы измените их с помощью override_settings, значение изменится, если вы получите к нему доступ через модуль django.conf.settings, но внутренние компоненты Django обращаются к нему иначе. По сути, использование override_settings() или modify_settings() с этими настройками, вероятно, не даст ожидаемого результата.
Мы не рекомендуем изменять настройку DATABASES. Изменение настройки CACHES возможно, но немного сложно, если вы используете внутренние компоненты, которые используют кеширование, например, django.contrib.sessions. Например, вам придётся повторно инициализировать бэкэнд сессий в тесте, который использует кэшированные сессии и переопределяет CACHES.
Наконец, избегайте алиасов ваших настроек в качестве констант уровня модуля, так как override_settings() не будет работать с такими значениями, поскольку они вычисляются только в первый раз при импорте модуля.
Также можно смоделировать отсутствие настройки, удалив её после переопределения настроек, как показано ниже:
@override_settings()
def test_something(self):
del settings.LOGIN_URL
...
При переопределении настроек убедитесь, что вы обрабатываете случаи, когда код вашего приложения использует кэш или аналогичную функцию, которая сохраняет состояние, даже если настройка изменена. Django предоставляет сигнал django.test.signals.setting_changed, который позволяет регистрировать обратные вызовы для очистки и иного сброса состояния при изменении настроек.
Сам Django использует этот сигнал для сброса различных данных:
| Переопределённые настройки | Сброшенные данные |
|---|---|
| USE_TZ, TIME_ZONE | Временная зона баз данных |
| TEMPLATES | Движки шаблонов |
| SERIALIZATION_MODULES | Кэш сериализаторов |
| LOCALE_PATHS, LANGUAGE_CODE | Стандартный перевод и загруженные переводы |
| MEDIA_ROOT, DEFAULT_FILE_STORAGE | Стандартное хранилище файлов |
Очистка тестового ящика вывода
Если вы используете любой из пользовательских классов TestCase Django, тестовый запуск очистит содержимое тестового ящика вывода электронной почты в начале каждого тестового случая.
Для получения более подробной информации об услугах электронной почты во время тестов см. Службы электронной почты ниже.
Утверждения
Так как стандартный класс Python unittest.TestCase реализует методы утверждения, такие как assertTrue() и assertEqual(), пользовательский класс Django TestCase предоставляет ряд пользовательских методов утверждения, полезных для тестирования веб-приложений:
Сообщения об ошибках, предоставляемые большинством этих методов утверждения, можно настроить с помощью аргумента msg_prefix. Эта строка будет добавлена в начало любого сообщения об ошибке, сгенерированного утверждением. Это позволяет предоставить дополнительную информацию, которая может помочь определить местоположение и причину ошибки в вашем наборе тестов.
-
SimpleTestCase.assertRaisesMessage(expected_exception, expected_message, callable, *args, **kwargs)[source] -
SimpleTestCase.assertRaisesMessage(expected_exception, expected_message) -
Утверждает, что выполнение
callableвызываетexpected_exception, и чтоexpected_messageсодержится в сообщении об исключении. Любой другой результат рассматривается как ошибка. Это упрощенная версияunittest.TestCase.assertRaisesRegex(), в которойexpected_messageне обрабатывается как регулярное выражение.Если заданы только параметры
expected_exceptionиexpected_message, возвращается менеджер контекста, чтобы код, который тестируется, можно было записать встроенным образом, а не в виде функции:with self.assertRaisesMessage(ValueError, 'invalid literal for int()'): int('a')Устарело начиная с версии 1.9: Передача
callableв качестве ключевого аргумента с именемcallable_objустарела. Передайте вызываемый объект в качестве позиционного аргумента вместо этого.
-
SimpleTestCase.assertFieldOutput(fieldclass, valid, invalid, field_args=None, field_kwargs=None, empty_value='')[source] -
Утверждает, что поле формы ведет себя правильно с различными входными данными.
Параметры: - fieldclass – класс поля, подлежащего тестированию.
- valid – словарь, сопоставляющий допустимые входные данные с ожидаемыми очищенными значениями.
- invalid – словарь, сопоставляющий недопустимые входные данные с одним или несколькими сообщениями об ошибках.
- field_args – аргументы, передаваемые для инициализации поля.
- field_kwargs – ключевые аргументы, передаваемые для инициализации поля.
-
empty_value – ожидаемый результат очистки для входных данных в
empty_values.
Например, следующий код проверяет, что поле
EmailFieldпринимаетa@a.comкак допустимый адрес электронной почты, но отклоняетaaaс соответствующим сообщением об ошибке:self.assertFieldOutput(EmailField, {'a@a.com': 'a@a.com'}, {'aaa': ['Enter a valid email address.']})
-
SimpleTestCase.assertFormError(response, form, field, errors, msg_prefix='')[source] -
Утверждает, что поле на форме вызывает предоставленный список ошибок при отображении на форме.
form— имя экземпляраFormв контексте шаблона.field— имя поля на форме, которое проверяется. Еслиfieldимеет значениеNone, будут проверены внепольные ошибки (ошибки, к которым можно получить доступ черезform.non_field_errors()).errors— строка ошибки или список строк ошибок, которые ожидаются в результате проверки формы.
-
SimpleTestCase.assertFormsetError(response, formset, form_index, field, errors, msg_prefix='')[source] -
Утверждает, что
formsetвызывает предоставленный список ошибок при отображении.formset— имя экземпляраFormsetв контексте шаблона.form_index— номер формы вFormset. Еслиform_indexимеет значениеNone, будут проверены внеформенные ошибки (ошибки, к которым можно получить доступ черезformset.non_form_errors())field— имя поля на форме для проверки. Еслиfieldимеет значениеNone, будут проверены внепольные ошибки (ошибки, к которым можно получить доступ черезform.non_field_errors()).errors— строка ошибки или список строк ошибок, которые ожидаются в результате проверки формы.
-
SimpleTestCase.assertContains(response, text, count=None, status_code=200, msg_prefix='', html=False)[source] -
Утверждает, что экземпляр
Responseвыдал указанноеstatus_codeи чтоtextпоявляется в содержимом ответа. Еслиcountуказано,textдолжно встречаться ровноcountраз в ответе.Установите
htmlвTrueдля обработкиtextкак HTML. Сравнение с содержимым ответа будет основано на семантике HTML, а не на точном равенстве символов. Пробелы игнорируются в большинстве случаев, порядок атрибутов не имеет значения. См.assertHTMLEqual()для получения дополнительной информации.
-
SimpleTestCase.assertNotContains(response, text, status_code=200, msg_prefix='', html=False)[source] -
Утверждает, что экземпляр
Responseвыдал указанноеstatus_codeи чтоtextне появляется в содержимом ответа.Установите
htmlвTrueдля обработкиtextкак HTML. Сравнение с содержимым ответа будет основано на семантике HTML, а не на точном равенстве символов. Пробелы игнорируются в большинстве случаев, порядок атрибутов не имеет значения. См.assertHTMLEqual()для получения дополнительной информации.
-
SimpleTestCase.assertTemplateUsed(response, template_name, msg_prefix='', count=None)[source] -
Утверждает, что шаблон с заданным именем использовался при отрисовке ответа.
Имя — строка, например
'admin/index.html'.Аргумент count — целое число, указывающее количество раз, когда шаблон должен быть отрисован. Значение по умолчанию —
None, что означает, что шаблон должен быть отрисован один или несколько раз.Можно использовать его как менеджер контекста, например так:
with self.assertTemplateUsed('index.html'): render_to_string('index.html') with self.assertTemplateUsed(template_name='index.html'): render_to_string('index.html')
-
SimpleTestCase.assertTemplateNotUsed(response, template_name, msg_prefix='')[source] -
Утверждает, что шаблон с заданным именем не использовался при отрисовке ответа.
Можно использовать его как менеджер контекста аналогично
assertTemplateUsed().
-
SimpleTestCase.assertRedirects(response, expected_url, status_code=302, target_status_code=200, msg_prefix='', fetch_redirect_response=True)[source] -
Утверждает, что ответ вернул переадресацию статуса
status_code, перенаправил наexpected_url(включая любыеGETданные), и что конечная страница была получена со статусомtarget_status_code.Если в вашем запросе использовался аргумент
follow,expected_urlиtarget_status_codeбудут URL и кодом состояния для конечной точки цепочки переадресации.Если
fetch_redirect_responseравноFalse, конечная страница не будет загружена. Поскольку тестовый клиент не может получать доступ к внешним URL-адресам, это особенно полезно, еслиexpected_urlне является частью приложения Django.Схема обрабатывается правильно при сравнении двух URL-адресов. Если в местонахождении, куда мы перенаправлены, нет схемы, используется схема исходного запроса. Если присутствует, схема в
expected_urlиспользуется для сравнения.Устарело начиная с версии 1.9: Аргумент
hostустарел, так как перенаправления больше не принудительно преобразуются в абсолютные URL-адреса.
-
SimpleTestCase.assertHTMLEqual(html1, html2, msg=None)[source] -
Утверждает, что строки
html1иhtml2равны. Сравнение основано на семантике HTML. Сравнение учитывает следующие моменты:- Пробелы перед и после тегов HTML игнорируются.
- Все типы пробелов считаются эквивалентными.
- Все открывающие теги закрываются неявно, например, когда закрывается окружающий тег или заканчивается документ HTML.
- Пустые теги эквивалентны их вариантам с самозакрытием.
- Порядок атрибутов HTML-элемента не имеет значения.
- Атрибуты без аргумента равны атрибутам, которые равны по имени и значению (см. примеры).
Следующие примеры являются допустимыми тестами и не вызывают
AssertionError:self.assertHTMLEqual( '<p>Hello <b>world!</p>', '''<p> Hello <b>world! <b/> </p>''' ) self.assertHTMLEqual( '<input type="checkbox" checked="checked" id="id_accept_terms" />', '<input id="id_accept_terms" type="checkbox" checked>' )html1иhtml2должны быть валидным HTML. Будет поднятоAssertionError, если один из них не может быть проанализирован.Вывод в случае ошибки можно настроить с помощью аргумента
msg.
-
SimpleTestCase.assertHTMLNotEqual(html1, html2, msg=None)[source] -
Проверяет, что строки
html1иhtml2не равны. Сравнение основано на HTML-семантике. Подробности см. вassertHTMLEqual().html1иhtml2должны быть валидным HTML. Будет поднято исключениеAssertionError, если одна из них не может быть обработана.Вывод в случае ошибки можно настроить с помощью аргумента
msg.
-
SimpleTestCase.assertXMLEqual(xml1, xml2, msg=None)[source] -
Проверяет, что строки
xml1иxml2равны. Сравнение основано на XML-семантике. АналогичноassertHTMLEqual(), сравнение выполняется на обработанном содержимом, поэтому учитываются только семантические различия, а не синтаксические. При передаче некорректного XML в любой параметр, всегда генерируется исключениеAssertionError, даже если обе строки идентичны.Вывод в случае ошибки можно настроить с помощью аргумента
msg.
-
SimpleTestCase.assertXMLNotEqual(xml1, xml2, msg=None)[source] -
Проверяет, что строки
xml1иxml2не равны. Сравнение основано на XML-семантике. Подробности см. вassertXMLEqual().Вывод в случае ошибки можно настроить с помощью аргумента
msg.
-
SimpleTestCase.assertInHTML(needle, haystack, count=None, msg_prefix='')[source] -
Проверяет, что HTML-фрагмент
needleсодержится вhaystack.Если указан целочисленный аргумент
count, то дополнительно будет строго проверено количествоneedle.Пробелы в большинстве случаев игнорируются, а порядок атрибутов не важен. Передаваемые аргументы должны быть валидным HTML.
-
SimpleTestCase.assertJSONEqual(raw, expected_data, msg=None)[source] -
Проверяет, что JSON-фрагменты
rawиexpected_dataравны. Обычные правила JSON, игнорирующие пробелы, применяются, поскольку обработка делегируется библиотекеjson.Вывод в случае ошибки можно настроить с помощью аргумента
msg.
-
SimpleTestCase.assertJSONNotEqual(raw, expected_data, msg=None)[source] -
Проверяет, что JSON-фрагменты
rawиexpected_dataне равны. См.assertJSONEqual()для получения дополнительных подробностей.Вывод в случае ошибки можно настроить с помощью аргумента
msg.
-
TransactionTestCase.assertQuerysetEqual(qs, values, transform=repr, ordered=True, msg=None)[source] -
Проверяет, что результат запроса
qsвозвращает определённый список значенийvalues.Сравнение содержимого
qsиvaluesвыполняется с помощью функцииtransform; по умолчанию это означает, что сравниваютсяrepr()каждого значения. Любая другая вызываемая функция может быть использована, еслиrepr()не предоставляет уникального или полезного сравнения.По умолчанию сравнение также зависит от порядка. Если
qsне предоставляет неявного порядка, вы можете установить параметрorderedв значениеFalse, что превращает сравнение вcollections.Counterсравнение. Если порядок не определён (если заданноеqsне упорядочено и сравнение выполняется с более чем одним упорядоченным значением), возникает исключениеValueError.Вывод в случае ошибки можно настроить с помощью аргумента
msg.
-
TransactionTestCase.assertNumQueries(num, func, *args, **kwargs)[source] -
Проверяет, что при вызове
funcс*argsи**kwargsвыполняетсяnumбаз данных запросов.Если ключ
"using"присутствует вkwargs, он используется в качестве алиаса базы данных для проверки количества запросов. Если вы хотите вызвать функцию с параметромusingвы можете сделать это, обернув вызов вlambdaдля добавления дополнительного параметра:self.assertNumQueries(7, lambda: my_function(using=7))
Вы также можете использовать его как менеджер контекста:
with self.assertNumQueries(2): Person.objects.create(name="Aaron") Person.objects.create(name="Daniel")
Сервисы электронной почты
Если ваши Django-представления отправляют электронную почту с использованием функциональности отправки почты Django, вы, вероятно, не хотите отправлять почту каждый раз, когда запускаете тест, использующий данную представление. По этой причине запуск тестов Django автоматически перенаправляет всю отправленную Django почту в фиктивный буфер отправки. Это позволяет проверить все аспекты отправки почты – от количества отправленных сообщений до содержимого каждого сообщения – без фактической отправки.
Запуск тестов достигает этого, прозрачно заменив обычный бэкенд электронной почты бэкендом для тестирования. (Не беспокойтесь – это не влияет на другие почтовые отправители за пределами Django, такие как почтовый сервер вашего компьютера, если вы его используете.)
-
django.core.mail.outbox
Во время запуска тестов каждое отправленное сообщение сохраняется в django.core.mail.outbox. Это простой список всех EmailMessage экземпляров, которые были отправлены. Атрибут outbox — это специальный атрибут, создаваемый только при использовании бэкенда электронной почты для тестирования. Он обычно не существует как часть модуля django.core.mail и его нельзя напрямую импортировать. Приведенный ниже код демонстрирует, как правильно получить доступ к этому атрибуту.
Вот пример теста, который проверяет django.core.mail.outbox на длину и содержимое:
from django.core import mail
from django.test import TestCase
class EmailTest(TestCase):
def test_send_email(self):
# Send message.
mail.send_mail(
'Subject here', 'Here is the message.',
'from@example.com', ['to@example.com'],
fail_silently=False,
)
# Test that one message has been sent.
self.assertEqual(len(mail.outbox), 1)
# Verify that the subject of the first message is correct.
self.assertEqual(mail.outbox[0].subject, 'Subject here')
Как отмечалось ранее, буфер отправки тестов очищается в начале каждого теста в Django *TestCase. Чтобы очистить буфер отправки вручную, присвойте пустой список переменной mail.outbox:
from django.core import mail # Empty the test outbox mail.outbox = []
Команды управления
Команды управления можно протестировать с помощью функции call_command(). Вывод можно перенаправить в экземпляр StringIO:
from django.core.management import call_command
from django.test import TestCase
from django.utils.six import StringIO
class ClosepollTest(TestCase):
def test_command_output(self):
out = StringIO()
call_command('closepoll', stdout=out)
self.assertIn('Expected output', out.getvalue())
Пропуск тестов
Библиотека unittest предоставляет декораторы @skipIf и @skipUnless, которые позволяют пропускать тесты, если заранее известно, что эти тесты могут потерпеть неудачу при определённых условиях.
Например, если ваш тест требует определённой дополнительной библиотеки для успеха, вы можете украсить тестовый случай декоратором @skipIf. Затем запуск тестов сообщит, что тест не был выполнен и почему, вместо того чтобы завершить тест неудачей или пропустить его вовсе.
Для дополнения этих поведений по пропусканию тестов Django предоставляет два дополнительных декоратора пропуска. Вместо проверки общего булевого значения, эти декораторы проверяют возможности базы данных и пропускают тест, если база данных не поддерживает определённую именованную функцию.
Декораторы используют строковый идентификатор для описания функций базы данных. Эта строка соответствует атрибутам класса функций подключения к базе данных. См. класс django.db.backends.BaseDatabaseFeatures для получения полного списка функций базы данных, которые могут быть использованы в качестве основы для пропуска тестов.
-
skipIfDBFeature(*feature_name_strings)[source]
Пропустить декорированный тест или TestCase если все именованные функции базы данных поддерживаются.
Например, следующий тест не будет выполнен, если база данных поддерживает транзакции (например, он не будет запущен под PostgreSQL, но будет под MySQL с таблицами MyISAM):
class MyTests(TestCase):
@skipIfDBFeature('supports_transactions')
def test_transaction_behavior(self):
# ... conditional test code
pass
skipIfDBFeature может принимать несколько строк функций.
-
skipUnlessDBFeature(*feature_name_strings)[source]
Пропустить декорированный тест или TestCase если какие-либо из указанных баз данных не поддерживаются.
Например, следующий тест будет выполнен только в том случае, если база данных поддерживает транзакции (например, он будет запущен под PostgreSQL, но не под MySQL с таблицами MyISAM):
class MyTests(TestCase):
@skipUnlessDBFeature('supports_transactions')
def test_transaction_behavior(self):
# ... conditional test code
pass
skipUnlessDBFeature может принимать несколько строк функций.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.9/topics/testing/tools/