Инструменты тестирования
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. Это помогает быстро выполнять unit-тесты.
-
При получении страниц помните, что нужно указывать путь URL, а не весь домен. Например, это правильно:
>>> c.get('/login/')Это неправильно:
>>> c.get('https://www.example.com/login/')Клиент тестирования не может получать веб-страницы, которые не работают на вашем проекте Django. Если вам нужно получить другие веб-страницы, используйте модуль стандартной библиотеки Python, такой как
urllib. - Для разрешения URL-адресов клиент тестирования использует URLconf, указанный в настройке
ROOT_URLCONF. -
Хотя приведенный выше пример будет работать в интерактивном интерпретаторе Python, некоторые функции клиента тестирования, особенно связанные с шаблонами, доступны только во время выполнения тестов.
Причина в том, что тестовый запуск Django выполняет немного «волшебства», чтобы определить, какой шаблон был загружен данным представлением. Это «волшебство» (по существу, патчинг системы шаблонов Django в памяти) происходит только во время выполнения тестов.
-
По умолчанию клиент тестирования отключит все проверки 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-запросами. Самый простой способ сделать это — вручную закрыть файл после того, как он был предоставлен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()тестового клиента для симуляции входа пользователя на сайт.После вызова этого метода, тестовый клиент будет содержать все необходимые куки и данные сессии для прохождения любых тестов, связанных с входом в систему, которые могут быть частью представления.
Формат аргумента
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()для создания нового пользователя с правильно хешированным паролем.Изменено в Django 1.10:В предыдущих версиях неактивные пользователи (
is_active=False) не могли войти в систему.
-
force_login(user, backend=None)[source] -
Добавлено в Django 1.9.
Если ваш сайт использует систему аутентификации 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) -
Добавлено в Django 1.9.
Тело ответа, проанализированное как 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()
Указание языка
При тестировании приложений, поддерживающих международный язык и локализацию, вам, возможно, понадобится установить язык для запроса тестового клиента. Способ установки зависит от того, включен ли LocaleMiddleware.
Если middleware включен, язык можно установить, создав cookie с именем LANGUAGE_COOKIE_NAME и значением кода языка:
from django.conf import settings
def test_language_using_cookie(self):
self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: 'fr'})
response = self.client.get('/')
self.assertEqual(response.content, b"Bienvenue sur mon site.")
или включив заголовок Accept-Language HTTP в запросе:
def test_language_using_header(self):
response = self.client.get('/', HTTP_ACCEPT_LANGUAGE='fr')
self.assertEqual(response.content, b"Bienvenue sur mon site.")
Более подробная информация приведена в Как Django определяет предпочтение языка.
Если middleware не включен, активный язык можно установить с помощью translation.override():
from django.utils import translation
def test_language_using_override(self):
with translation.override('fr'):
response = self.client.get('/')
self.assertEqual(response.content, b"Bienvenue sur mon site.")
Более подробная информация приведена в Явное указание активного языка.
Пример
Ниже представлен простой юнит-тест с использованием тестового клиента:
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 предоставляет несколько расширений этого базового класса:
Иерархия классов тестирования 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.
Если ваши тесты выполняют запросы к базе данных, используйте подклассы TransactionTestCase или TestCase.
-
SimpleTestCase.allow_database_queries -
Добавлено в Django 1.9.
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.
TransactionTestCase и TestCase идентичны, за исключением способа сброса базы данных до известного состояния и возможности кода теста проверять эффекты commit и rollback:
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()
cls.selenium.implicitly_wait(10)
@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 и загружена ли следующая страница перед продолжением дальнейшего выполнения теста. Сделайте это, например, ожидая, пока в ответе не будет найден тег <body> HTML (требуется 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. Этот клиент создается заново для каждого теста, поэтому вам не нужно беспокоиться о сохранении состояния (например, cookie) от одного теста к другому.
Это означает, что вместо создания экземпляра 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
Если ваше приложение предоставляет представления, вы можете захотеть включить тесты, которые используют тестовый клиент для тестирования этих представлений. Однако конечный пользователь может развернуть представления вашего приложения по любому URL-адресу по своему выбору. Это означает, что ваши тесты не могут полагаться на тот факт, что ваши представления будут доступны по определённому URL-адресу. Для настройки URLconf используйте декоратор @override_settings(ROOT_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 также влияет на базы данных, в которые загружаются 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={
'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={
'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={
'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 | Стандартное хранилище файлов |
Очистка тестового ящика вывода
Если вы используете любой из пользовательских классов Django TestCase, запуск тестов очистит содержимое тестового ящика вывода электронной почты в начале каждого тестового случая.
Для получения дополнительной информации об email-сервисах во время тестов см. Email-сервисы ниже.
Утверждения
Поскольку стандартный 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")
Тегирование тестов
Вы можете добавить теги к вашим тестам, чтобы легко запускать определённый подмножество. Например, вы можете пометить быстрые или медленные тесты:
from django.test import tag
class SampleTestCase(TestCase):
@tag('fast')
def test_fast(self):
...
@tag('slow')
def test_slow(self):
...
@tag('slow', 'core')
def test_slow_but_core(self):
...
Вы также можете добавить тег к тестовому случаю:
@tag('slow', 'core')
class SampleTestCase(TestCase):
...
Затем вы можете выбрать, какие тесты запускать. Например, чтобы запустить только быстрые тесты:
$ ./manage.py test --tag=fast
Или чтобы запустить быстрые тесты и основной (даже если он медленный):
$ ./manage.py test --tag=fast --tag=core
Вы также можете исключить тесты по тегу. Чтобы запустить тесты core, если они не медленные:
$ ./manage.py test --tag=core --exclude-tag=slow
test --exclude-tag имеет приоритет перед test --tag, поэтому если у теста есть два тега, и вы выбираете один из них, а исключаете другой, тест не будет запущен.
Сервисы электронной почты
Если ваши 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
-
skipUnlessDBFeature(*feature_name_strings)[source]
Пропустить декорированный тест или TestCase если какая-либо из именованных функций базы данных не поддерживается.
Например, следующий тест будет выполнен только в том случае, если база данных поддерживает транзакции (например, он будет выполнен под PostgreSQL, но не под MySQL с таблицами MyISAM):
class MyTests(TestCase):
@skipUnlessDBFeature('supports_transactions')
def test_transaction_behavior(self):
# ... conditional test code
pass
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.10/topics/testing/tools/