Инструменты тестирования
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('http://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-Agentв каждом запросе:>>> 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] -
Аргумент
secureбыл добавлен.Выполняет запрос 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будут закодированы как многочастотное сообщение и использованы для создания данных запроса 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 2616, который предписывает, что запросы TRACE не должны иметь тела.Аргументы
follow,secure, иextraдействуют так же, как и дляClient.get().
-
login(**credentials)[source] -
Если ваш сайт использует систему аутентификации Django, и вы работаете с входом пользователей, вы можете использовать метод test client’s
login()для имитации входа пользователя на сайт.Неактивные пользователи (
is_active=False) не допускаются к входу, так как этот метод эквивалентен представлениюlogin(), которое используетAuthenticationFormи поэтому по умолчанию отклоняет неактивных пользователей.После вызова этого метода, у test client будут все необходимые куки и данные сессии для прохождения любых тестов, основанных на входе в систему, которые могут быть частью представления.
Формат аргумента
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), либо с помощью тестового фикстура. Помните, что если вы хотите, чтобы ваш тестовый пользователь имел пароль, вы не можете установить пароль пользователя, установив атрибут пароля напрямую — вы должны использовать функцию
set_password()для хранения правильно хэшированного пароля. В качестве альтернативы, вы можете использовать вспомогательный методcreate_user()для создания нового пользователя с правильно хэшированным паролем.
-
logout()[source] -
Если ваш сайт использует систему аутентификации Django, метод
logout()может использоваться для имитации выхода пользователя из вашего сайта.После вызова этого метода, у test client будут очищены все куки и данные сессии по умолчанию. Последующие запросы будут казаться исходящими от
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может быть подходящей альтернативой в ответах с этим атрибутом.
-
request -
Данные запроса, которые стимулировали ответ.
-
wsgi_request -
Экземпляр
WSGIRequest, сгенерированный обработчиком тестов, который сгенерировал ответ.
-
status_code -
HTTP статус ответа, как целое число. См. RFC 2616#section-10 для полного списка кодов HTTP статуса.
-
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 предоставляет несколько расширений этого базового класса:
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.
Если вам нужны другие, более сложные и ресурсоёмкие функции Django, такие как:
- Тестирование или использование ORM.
- База данных
fixtures. - Пропуск тестов в зависимости от возможностей бэкенда базы данных по базе данных.
- Остальные специализированные методы
assert*.
то вам следует использовать TransactionTestCase или TestCase.
SimpleTestCase наследуется от unittest.TestCase.
Предупреждение
SimpleTestCase и его подклассы (например, TestCase, ...) полагаются на setUpClass() и tearDownClass() для выполнения некоторой инициализации на уровне класса (например, переопределение настроек). Если вам необходимо переопределить эти методы, не забудьте вызвать реализацию super:
class MyTestCase(TestCase):
@classmethod
def setUpClass(cls):
super(MyTestCase, cls).setUpClass() # Call parent first
...
@classmethod
def tearDownClass(cls):
...
super(MyTestCase, cls).tearDownClass() # Call parent last
TransactionTestCase
-
class TransactionTestCase[source]
Класс TestCase Django (описанный ниже) использует возможности транзакций базы данных, чтобы ускорить процесс сброса базы данных до известного состояния в начале каждого теста. Однако следствием этого является то, что некоторые особенности базы данных не могут быть протестированы в классе Django TestCase. Например, вы не можете проверить, что блок кода выполняется в рамках транзакции, как это требуется при использовании select_for_update(). В этих случаях вы должны использовать TransactionTestCase.
В более старых версиях Django эффекты коммита и отката транзакции не могли быть проверены в TestCase. С завершением отмены устаревшего способа управления транзакциями в Django 1.8 команды управления транзакциями (например, transaction.commit()) больше не отключаются внутри TestCase.
TransactionTestCase и TestCase идентичны, за исключением способа сброса базы данных до известного состояния и возможности для кода теста проверить эффекты коммита и отката:
TransactionTestCaseсбрасывает базу данных после выполнения теста, обнуляя все таблицы.TransactionTestCaseможет вызвать коммит и откат и наблюдать эффекты этих вызовов на базе данных.TestCase, с другой стороны, не обнуляет таблицы после теста. Вместо этого он заключает код теста в транзакцию базы данных, которая отменяется в конце теста. Это гарантирует, что откат в конце теста восстановит базу данных до первоначального состояния.
Предупреждение
TestCase работающий на базе данных, не поддерживающей откат (например, MySQL с движком хранения MyISAM), и все экземпляры TransactionTestCase, откатываются в конце теста, удаляя все данные из тестовой базы данных и перезагружая начальные данные для приложений без миграций.
Приложения с миграциями не будут видеть свои данные перезагруженными; если вам нужна эта функциональность (например, сторонние приложения должны её включить), вы можете установить serialized_rollback = True внутри блока TestCase.
TransactionTestCase наследуется от SimpleTestCase.
TestCase
-
class TestCase[source]
Этот класс предоставляет некоторые дополнительные возможности, которые могут быть полезны для тестирования веб-сайтов.
Преобразование обычного unittest.TestCase в Django TestCase просто: достаточно изменить базовый класс вашего теста с 'unittest.TestCase' на 'django.test.TestCase'. Все стандартные возможности Python unit test останутся доступными, но будут дополнены полезными функциями, включая:
- Автоматическую загрузку фикстур.
- Обёртку тестов в два вложенных
atomicблока: один для всего класса и один для каждого теста. - Создание экземпляра TestClient.
- Специфичные для Django утверждения для проверки перенаправлений и ошибок форм.
-
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(), например.
Предупреждение
Если вы хотите протестировать конкретное поведение транзакций базы данных, используйте TransactionTestCase, так как TestCase оборачивает выполнение теста в atomic() блок.
TestCase наследуется от TransactionTestCase.
LiveServerTestCase
-
class LiveServerTestCase[source]
LiveServerTestCase выполняет в основном то же, что и TransactionTestCase, но с одной дополнительной функцией: при настройке запускает активный сервер Django на заднем плане и останавливает его при завершении. Это позволяет использовать автоматические тестовые клиенты, отличные от виртуального клиента Django, такие как, например, клиент Selenium, для выполнения серии функциональных тестов внутри браузера и имитации действий реального пользователя.
По умолчанию адрес активного сервера 'localhost:8081', а полный URL можно получить во время тестов с помощью self.live_server_url. Если вы хотите изменить адрес по умолчанию (например, если порт 8081 уже занят), вы можете указать другой адрес команде test с помощью опции --liveserver, например:
$ ./manage.py test --liveserver=localhost:8082
Другой способ изменить адрес сервера по умолчанию — установить переменную среды 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; обратитесь к полной справке для получения более подробной информации.
В более ранних версиях LiveServerTestCase полагался на приложение staticfiles contrib app для прозрачной обработки статических файлов во время выполнения тестов. Эта функциональность была перенесена в подкласс StaticLiveServerTestCase, поэтому используйте этот подкласс, если вам нужна исходная работа.
LiveServerTestCase теперь просто публикует содержимое файловой системы под STATIC_ROOT по адресу STATIC_URL.
Примечание
При использовании в памяти 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. Этот клиент пересоздаётся для каждого теста, поэтому вам не нужно беспокоиться о сохранении состояния (такого как куки) от одного теста к другому.
Это означает, вместо инициализации 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
Тестовый случай для веб-сайта на базе данных не очень полезен, если в базе данных нет данных. Чтобы легко добавлять тестовые данные в базу данных, пользовательский класс Django TransactionTestCase предоставляет способ загрузки фикстур.
Фикстура — это набор данных, который Django знает, как импортировать в базу данных. Например, если на вашем сайте есть учётные записи пользователей, вы можете создать фикстуру с фиктивными учётными записями пользователей, чтобы заполнить вашу базу данных во время тестирования.
Самый простой способ создания фикстуры — использовать команду manage.py dumpdata. Это предполагает, что у вас уже есть какие-то данные в вашей базе данных. Подробности см. в dumpdata
documentation.
Примечание
Если вы когда-либо запускали manage.py migrate, вы уже использовали фикстуру, даже не подозревая об этом! Когда вы впервые вызываете migrate в базе данных, Django устанавливает фикстуру с именем initial_data. Это даёт вам возможность заполнить новую базу данных любыми начальными данными, такими как набор по умолчанию категорий.
Фикстуры с другими именами всегда можно установить вручную с помощью команды manage.py loaddata.
Начальные данные SQL и тестирование
Django предоставляет второй способ вставки начальных данных в модели — настраиваемый SQL-хук. Однако этот метод нельзя использовать для предоставления начальных данных для целей тестирования. Фреймворк тестирования Django очищает содержимое тестовой базы данных после каждого теста; в результате любые данные, добавленные с помощью настраиваемого SQL-хука, будут потеряны.
После создания фикстуры и размещения её в каталоге 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.
Эта процедура очистки/загрузки повторяется для каждого теста в тестовом случае, поэтому можно быть уверенным, что результат теста не будет зависеть от другого теста или от порядка выполнения тестов.
По умолчанию, фикстуры загружаются только в базу данных default. Если вы используете несколько баз данных и установили multi_db=True, фикстуры будут загружены во все базы данных.
Конфигурация URLconf
-
SimpleTestCase.urls
Устарело начиная с версии 1.8: Используйте @override_settings(ROOT_URLCONF=...) для конфигурации URLconf.
Если ваше приложение предоставляет представления, вы можете захотеть включить тесты, которые используют клиент тестирования для проверки этих представлений. Однако конечный пользователь свободен развернуть представления вашего приложения в любом 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/')
Ранее override_settings импортировался из django.test.utils.
-
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содержится в сообщении об исключении. Любой другой результат считается ошибкой. Аналогично утверждению unittestassertRaisesRegex()с той разницей, чтоexpected_messageне является регулярным выражением.Если заданы только параметры
expected_exceptionиexpected_message, возвращает менеджер контекста, чтобы код, который тестируется, можно было записать в одну строку, а не как функцию:with self.assertRaisesMessage(ValueError, 'invalid literal for int()'): int('a')
-
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, host=None, msg_prefix='', fetch_redirect_response=True)[source] -
Утверждает, что ответ вернул статус перенаправления
status_code, перенаправив наexpected_url(включая любыеGETданные), и что конечная страница была получена со статусомtarget_status_code.Если ваш запрос использовал аргумент
follow,expected_urlиtarget_status_codeбудут URL и кодом состояния для конечной точки цепочки перенаправлений.Аргумент
hostзадаёт значение хоста по умолчанию, еслиexpected_urlего не содержит (например,"/bar/"). Еслиexpected_urlявляется абсолютным URL, который включает хост (например,"http://testhost/bar/"), параметрhostбудет проигнорирован. Обратите внимание, что клиент тестирования не поддерживает загрузку внешних URL-адресов, но параметр может быть полезен, если вы тестируете с пользовательским хостом HTTP (например, инициализируя клиент теста сClient(HTTP_HOST="testhost")).Если
fetch_redirect_responseравноFalse, конечная страница не будет загружена. Поскольку клиент тестирования не может загружать внешние URL-адреса, это особенно полезно, еслиexpected_urlне является частью вашего приложения Django.Схема обрабатывается корректно при сравнении двух URL. Если в место назначения перенаправления не указана схема, используется схема исходного запроса. Если присутствует, схема в
expected_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.Метод теперь принимает параметр
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 — это специальный атрибут, созданный только при использовании бэкенда электронной почты locmem. Обычно он не существует как часть модуля 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 теперь можно использовать для декорирования класса TestCase.
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 теперь можно использовать для декорирования класса TestCase.
skipUnlessDBFeature теперь может принимать несколько строк функций.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.8/topics/testing/tools/