Инструменты тестирования ¶
Django предоставляет небольшой набор инструментов, которые пригодятся при написании тестов.
Тестовый клиент ¶
Тестовый клиент - это класс Python, который действует как фиктивный веб-браузер, позволяя вам тестировать свои представления и программно взаимодействовать с вашим приложением на Django.
Вот некоторые из вещей, которые вы можете делать с помощью тестового клиента:
- Имитируйте запросы GET и POST по URL-адресу и наблюдайте за ответом - все, от низкоуровневого HTTP (заголовки результатов и коды состояния) до содержимого страницы.
- Просматривайте цепочку перенаправлений (если есть) и проверяйте URL и код статуса на каждом шаге.
- Проверьте, что данный запрос обрабатывается заданным шаблоном Django с контекстом шаблона, который содержит определенные значения.
Обратите внимание, что тестовый клиент не предназначен для замены Selenium или других встроенных в браузер фреймворков. У тестового клиента Django другой фокус. Коротко:
- Используйте тестовый клиент Django, чтобы убедиться, что отображается правильный шаблон и что ему передаются правильные данные контекста.
- Используйте встроенные в браузер фреймворки, такие как Selenium, для тестирования визуализированного HTML и
поведения веб-страниц, а именно функциональности JavaScript. Django также предоставляет специальную поддержку для этих фреймворков; см. раздел
LiveServerTestCase
для более подробной информации.
Комплексный набор тестов должен использовать комбинацию обоих типов тестов.
Обзор и быстрый пример ¶
Чтобы использовать тестовый клиент, создайте django.test.Client
и извлеките веб-страницы:
>>> from django.test import Client
>>> c = Client()
>>> response = c.post('/login/', {'username': 'john', 'password': 'smith'})
>>> response.status_code
200
>>> response = c.get('/customer/details/')
>>> response.content
b'<!DOCTYPE html...'
Как предполагает этот пример, вы можете создать экземпляр Client
из сеанса интерактивного интерпретатора Python.
Обратите внимание на несколько важных моментов в работе тестового клиента:
Тестовый клиент не требует, чтобы веб-сервер был запущен. Фактически, он будет работать нормально даже без запущенного веб-сервера! Это потому, что он избегает накладных расходов HTTP и имеет дело непосредственно с фреймворком Django. Это помогает ускорить выполнение модульных тестов.
При получении страниц не забудьте указать путь URL-адреса, а не весь домен. Например, это правильно:
>>> c.get('/login/')
Это неверно:
>>> c.get('https://www.example.com/login/')
Тестовый клиент не может получать веб-страницы, которые не поддерживаются вашим проектом Django. Если вам нужно получить другие веб-страницы, используйте модуль стандартной библиотеки Python, например
urllib
.Для разрешения URL-адресов тестовый клиент использует любой URLconf, на который указывает ваш
ROOT_URLCONF
параметр.Хотя приведенный выше пример будет работать в интерактивном интерпретаторе Python, некоторые функции тестового клиента, особенно связанные с шаблоном, доступны только во время выполнения тестов .
Причина этого в том, что средство запуска тестов Django выполняет немного черной магии, чтобы определить, какой шаблон был загружен данным представлением. Эта черная магия (по сути, исправление системы шаблонов Django в памяти) происходит только во время выполнения теста.
По умолчанию тестовый клиент отключит все проверки CSRF, выполняемые вашим сайтом.
Если по какой-то причине вы хотите, чтобы тестовый клиент выполнял проверки CSRF, вы можете создать экземпляр тестового клиента, который выполняет проверки CSRF. Для этого передайте
enforce_csrf_checks
аргумент при создании клиента:>>> from django.test import Client >>> csrf_client = Client(enforce_csrf_checks=True)
Делать запросы ¶
Используйте django.test.Client
класс, чтобы делать запросы.
-
class
Client
( enforce_csrf_checks = False , json_encoder = DjangoJSONEncoder , ** по умолчанию ) ¶ При строительстве не требует аргументов. Однако вы можете использовать аргументы ключевого слова, чтобы указать некоторые заголовки по умолчанию. Например,
User-Agent
в каждом запросе будет отправляться HTTP-заголовок:>>> c = Client(HTTP_USER_AGENT='Mozilla/5.0')
Значения из
extra
именованных аргументов , переданныхget()
,post()
и т.д. , имеют приоритет над дефолтами , переданных в конструктор класса.enforce_csrf_checks
Аргумент может быть использован для защиты тест CSRF (смотри выше).json_encoder
Аргумент позволяет устанавливать пользовательскую JSON кодер для сериализации JSON , который , описанной вpost()
.raise_request_exception
Аргумент позволяет контролировать ли или нет исключения , собранные во время запроса также должны быть подняты в ходе испытания. По умолчаниюTrue
.Когда у вас есть
Client
экземпляр, вы можете вызвать любой из следующих методов:-
get
( path , data = None , follow = False , secure = false , ** extra ) ¶ Делает запрос 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_ACCEPT='application/json')
… Отправит HTTP-заголовок
HTTP_ACCEPT
в подробное представление, что является хорошим способом тестирования путей кода, использующих этотdjango.http.HttpRequest.accepts()
метод.Спецификация CGI
Заголовки, отправленные через,
**extra
должны соответствовать спецификации CGI . Например, эмуляция другого заголовка «Host», отправленного в HTTP-запросе из браузера на сервер, должна передаваться какHTTP_HOST
.Если у вас уже есть аргументы GET в URL-кодированной форме, вы можете использовать эту кодировку вместо использования аргумента данных. Например, предыдущий запрос GET также может быть представлен как:
>>> c = Client() >>> c.get('/customers/details/?name=fred&age=7')
Если вы предоставите URL-адрес с закодированными данными GET и аргументом данных, аргумент данных будет иметь приоритет.
Если вы установите
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
( путь , данные = Нет , content_type = MULTIPART_CONTENT , follow = False , secure = False , ** дополнительно ) ¶ Выполняет запрос POST для предоставленного
path
и возвращаетResponse
объект, который задокументирован ниже.Пары ключ-значение в
data
словаре используются для отправки данных POST. Например:>>> c = Client() >>> c.post('/login/', {'name': 'fred', 'passwd': 'secret'})
… Приведет к оценке запроса POST на этот URL:
/login/
… С этими данными POST:
name=fred&passwd=secret
Если вы указываете
content_type
как application / json ,data
он сериализуется с использованиемjson.dumps()
словаря, списка или кортежа. Сериализация выполняетсяDjangoJSONEncoder
по умолчанию и может быть отменена путем предоставленияjson_encoder
аргументаClient
. Это сериализация также происходит из- заput()
,patch()
иdelete()
запросы.Если вы предоставите любой другой
content_type
(например, текст / xml для полезной нагрузки XML), содержимоеdata
отправляется как есть в запросе POST с использованиемcontent_type
вContent-Type
заголовке HTTP .Если вы не укажете значение для
content_type
, значенияdata
будут передаваться с типом содержимого multipart / form-data . В этом случае пары ключ-значениеdata
будут закодированы как составное сообщение и использоваться для создания полезных данных POST.Чтобы отправить несколько значений для данного ключа - например, чтобы указать выбор для a - предоставьте значения в виде списка или кортежа для требуемого ключа. Например, это значение отправит три выбранных значения для поля с именем :
<select multiple>
data
choices
{'choices': ('a', 'b', 'd')}
Отправка файлов - особый случай. Чтобы отправить файл POST, вам нужно только указать имя поля файла в качестве ключа и дескриптор файла, который вы хотите загрузить, в качестве значения. Например:
>>> c = Client() >>> with open('wishlist.doc', 'rb') as fp: ... c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp})
(Имя
attachment
здесь не имеет значения; используйте любое имя, ожидаемое вашим кодом обработки файлов.)Вы также можете предоставить любой файловый объект (например,
StringIO
илиBytesIO
) в качестве дескриптора файла. Если вы загружаете в объектImageField
, объекту нуженname
атрибут, который проходитvalidate_image_file_extension
валидатор. Например:>>> from io import BytesIO >>> img = BytesIO(b'mybinarydata') >>> img.name = 'myimage.jpg'
Обратите внимание: если вы хотите использовать один и тот же дескриптор файла для нескольких
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 ) ¶ Делает запрос HEAD для предоставленного
path
и возвращаетResponse
объект. Этот метод работает так же , какClient.get()
, в том числеfollow
,secure
иextra
аргументов, за исключением того, что он не возвращает тело сообщения.
-
options
( путь , данные = '' , content_type = 'application / octet-stream' , follow = False , secure = False , ** extra ) ¶ Создает запрос OPTIONS для предоставленного
path
и возвращаетResponse
объект. Полезно для тестирования интерфейсов RESTful.Если
data
предоставляется, он используется в качестве тела запроса, аContent-Type
заголовок имеет значениеcontent_type
.В
follow
,secure
иextra
аргументы действуют так же , как дляClient.get()
.
-
put
( путь , данные = '' , content_type = 'application / octet-stream' , follow = False , secure = False , ** extra ) ¶ Создает запрос PUT для предоставленного
path
и возвращаетResponse
объект. Полезно для тестирования интерфейсов RESTful.Если
data
предоставляется, он используется в качестве тела запроса, аContent-Type
заголовок имеет значениеcontent_type
.В
follow
,secure
иextra
аргументы действуют так же , как дляClient.get()
.
-
patch
( путь , данные = '' , content_type = 'application / octet-stream' , follow = False , secure = False , ** extra ) ¶ Создает запрос PATCH для предоставленного
path
и возвращаетResponse
объект. Полезно для тестирования интерфейсов RESTful.В
follow
,secure
иextra
аргументы действуют так же , как дляClient.get()
.
-
delete
( путь , данные = '' , content_type = 'application / octet-stream' , follow = False , secure = False , ** extra ) ¶ Делает запрос DELETE для предоставленного
path
и возвращаетResponse
объект. Полезно для тестирования интерфейсов RESTful.Если
data
предоставляется, он используется в качестве тела запроса, аContent-Type
заголовок имеет значениеcontent_type
.В
follow
,secure
иextra
аргументы действуют так же , как дляClient.get()
.
-
trace
( путь , следовать = False , secure = False , ** дополнительно ) ¶ Создает запрос TRACE для предоставленного
path
и возвращаетResponse
объект. Полезно для моделирования диагностических датчиков.В отличие от других методов запроса,
data
не предоставляется в качестве параметра ключевого слова, чтобы соответствоватьRFC 7231 # section-4.3.8 , который требует, чтобы запросы TRACE не имели тела.В
follow
,secure
иextra
аргументы действуют так же , как дляClient.get()
.
-
login
( ** учетные данные ) ¶ Если ваш сайт использует систему аутентификации Django и вы имеете дело с входом в систему пользователей, вы можете использовать метод тестового клиента
login()
для имитации эффекта входа пользователя на сайт.После вызова этого метода у тестового клиента будут все файлы cookie и данные сеанса, необходимые для прохождения любых тестов на основе входа в систему, которые могут составлять часть представления.
Формат
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()
вспомогательный метод для создания нового пользователя с правильно хешированным паролем.
-
force_login
( пользователь , backend = None ) ¶ Если ваш сайт использует систему аутентификации Django , вы можете использовать этот
force_login()
метод для имитации эффекта входа пользователя на сайт. Используйте этот метод вместо того,login()
когда тест требует, чтобы пользователь вошел в систему, и подробности того, как пользователь вошел в систему, не важны.В отличие от
login()
этого метода пропускаются этапы аутентификации и проверки: неактивным пользователям (is_active=False
) разрешается входить в систему, а учетные данные пользователя указывать не нужно.Для
backend
атрибута пользователя будет установлено значениеbackend
аргумента (которое должно быть строкой пути Python с точками) или,settings.AUTHENTICATION_BACKENDS[0]
если значение не указано.authenticate()
Функция вызываетсяlogin()
обычно помечает пользователя , как это.Этот метод быстрее, чем
login()
потому, что обходятся дорогостоящие алгоритмы хеширования паролей. Кроме того, вы можете ускоритьсяlogin()
, используя более слабый хешер при тестировании .
-
logout
() ¶ Если ваш сайт использует систему аутентификации Django , этот
logout()
метод можно использовать для имитации эффекта выхода пользователя из системы.После вызова этого метода тестовый клиент очистит все файлы cookie и данные сеанса до значений по умолчанию. Последующие запросы будут поступать из
AnonymousUser
.
-
Тестирование ответов ¶
get()
И post()
методы и возвращают Response
объект. Этот
Response
объект не совпадает с HttpResponse
объектом, возвращаемым представлениями Django; объект тестового ответа содержит некоторые дополнительные данные, полезные для проверки тестового кода.
В частности, Response
объект имеет следующие атрибуты:
-
класс
Response
¶ -
client
¶ Тестовый клиент, который использовался для выполнения запроса, в результате которого был получен ответ.
-
content
¶ Тело ответа в виде байтовой строки. Это последнее содержимое страницы, отображаемое представлением, или любое сообщение об ошибке.
-
context
¶ Context
Экземпляр шаблона, который использовался для визуализации шаблона, создавшего содержимое ответа.Если отображаемая страница использовала несколько шаблонов, то
context
будет списокContext
объектов в том порядке, в котором они были отрисованы.Независимо от количества шаблонов, используемых во время рендеринга, вы можете получить значения контекста с помощью
[]
оператора. Например, переменную контекстаname
можно получить, используя:>>> response = client.get('/foo/') >>> response.context['name'] 'Arthur'
Не используете шаблоны Django?
Этот атрибут заполняется только при использовании
DjangoTemplates
серверной части. Если вы используете другой шаблонизатор, этоcontext_data
может быть подходящей альтернативой для ответов с этим атрибутом.
-
exc_info
¶ Кортеж из трех значений, который предоставляет информацию о необработанном исключении, если таковое имеется, которое произошло во время просмотра.
Значения (тип, значение, трассировка) такие же, как и возвращаемые Python
sys.exc_info()
. Их значения:- type : тип исключения.
- значение : экземпляр исключения.
- traceback : объект трассировки, который инкапсулирует стек вызовов в точке, где изначально возникло исключение.
Если исключение не произошло, то
exc_info
будетNone
.
-
json
( ** kwargs ) ¶ Тело ответа в формате JSON. Дополнительные аргументы ключевого слова передаются
json.loads()
. Например:>>> response = client.get('/foo/') >>> response.json()['name'] 'Arthur'
Если
Content-Type
заголовка нет"application/json"
, тоValueError
при попытке синтаксического анализа ответа будет выдано сообщение.
-
request
¶ Данные запроса, которые стимулировали ответ.
-
wsgi_request
¶ WSGIRequest
Экземпляр генерируется тест - обработчик , который генерируется в ответ.
-
status_code
¶ HTTP-статус ответа в виде целого числа. Полный список определенных кодов см. В реестре кодов состояния IANA .
-
templates
¶ Список
Template
экземпляров, используемых для рендеринга окончательного содержимого, в порядке их рендеринга. Для каждого шаблона в списке используйте,template.name
чтобы получить имя файла шаблона, если шаблон был загружен из файла. (Имя представляет собой строку, например'admin/index.html'
.)Не используете шаблоны Django?
Этот атрибут заполняется только при использовании
DjangoTemplates
серверной части. Если вы используете другой механизм шаблонов, этоtemplate_name
может быть подходящей альтернативой, если вам нужно только имя шаблона, используемого для рендеринга.
-
resolver_match
¶ Экземпляр
ResolverMatch
для ответа. Вы можете использоватьfunc
атрибут, например, для проверки представления, которое обслужило ответ:# my_view here is a function based view self.assertEqual(response.resolver_match.func, my_view) # class-based views need to be compared by name, as the functions # generated by as_view() won't be equal self.assertEqual(response.resolver_match.func.__name__, MyView.as_view().__name__)
Если указанный URL-адрес не найден, доступ к этому атрибуту вызовет
Resolver404
исключение.
-
Как и в случае с обычным ответом, вы также можете получить доступ к заголовкам через
HttpResponse.headers
. Например, вы можете определить тип содержимого ответа, используя response.headers['Content-Type']
.
Исключения ¶
Если вы наведете тестовый клиент на представление, которое вызывает исключение и
Client.raise_request_exception
есть True
, это исключение будет видно в тестовом примере. Затем вы можете использовать стандартный блок или
для проверки исключений.try ... except
assertRaises()
Единственные исключения, которые не видны на тестовом клиенте являются
Http404
,
PermissionDenied
, SystemExit
, и
SuspiciousOperation
. Django перехватывает эти исключения внутренне и преобразует их в соответствующие коды ответа HTTP. В этих случаях вы можете проверить response.status_code
свой тест.
Если Client.raise_request_exception
есть False
, тестовый клиент возвращает 500 ответ , как будет возвращен в браузер. В ответе есть атрибут
exc_info
для предоставления информации о необработанном исключении.
Постоянное состояние ¶
Тестовый клиент отслеживает состояние. Если ответ возвращает куки, то , что куки будут храниться в тестовом клиенте и отправляется со всеми последующими 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
включен ли.
Если промежуточное ПО включено, язык можно установить, создав файл 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 обнаруживает языковые предпочтения .
Если промежуточное ПО не включено, активный язык можно установить с помощью
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 предоставляет несколько расширений этого базового класса:
Вы можете преобразовать нормаль unittest.TestCase
в любой из подклассов: измените базовый класс вашего теста с unittest.TestCase
на подкласс. Будут доступны все стандартные функции модульного тестирования Python, и они будут дополнены некоторыми полезными дополнениями, как описано в каждом разделе ниже.
SimpleTestCase
¶
-
класс
SimpleTestCase
¶
Подкласс, unittest.TestCase
который добавляет эту функциональность:
- Некоторые полезные утверждения, например:
- Проверяем, что вызываемый .
raises a certain exception
- Проверяем, что вызываемый .
triggers a certain warning
- Поле формы тестирования .
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
- Проверка
URLs
равенства двух . redirect
Приложение выполняет проверку HTTP .- Надежное тестирование двух
на равенство / неравенство или .
HTML fragments
containment
- Надежное тестирование двух
на равенство / неравенство.
XML fragments
- Надежная проверка двоих
на равенство.
JSON fragments
- Проверяем, что вызываемый .
- Возможность запускать тесты с измененными настройками .
- Использование .
client
Client
Если ваши тесты делают какие-либо запросы к базе данных, используйте подклассы
TransactionTestCase
или TestCase
.
-
SimpleTestCase.
databases
¶ SimpleTestCase
по умолчанию запрещает запросы к базе данных. Это помогает избежать выполнения запросов на запись, которые повлияют на другие тесты, поскольку каждыйSimpleTestCase
тест не запускается в транзакции. Если вас не беспокоит эта проблема, вы можете отключить это поведение, установив дляdatabases
атрибута class значение'__all__'
вашего тестового класса.
Предупреждение
SimpleTestCase
и его подклассы (например TestCase
,…) полагаются
setUpClass()
и tearDownClass()
выполняют некоторую инициализацию всего класса (например, переопределение настроек). Если вам нужно переопределить эти методы, не забудьте вызвать super
реализацию:
class MyTestCase(TestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
...
@classmethod
def tearDownClass(cls):
...
super().tearDownClass()
Обязательно учитывайте поведение Python, если во время возникает исключение
setUpClass()
. Если это произойдет, ни тесты в классе, ни
tearDownClass()
запускаются. В случае django.test.TestCase
, это приведет к утечке созданной транзакции, super()
что приведет к различным симптомам, включая ошибку сегментации на некоторых платформах (сообщается в macOS). Если вы хотите намеренно вызвать исключение, например
unittest.SkipTest
in setUpClass()
, обязательно сделайте это перед вызовом, super()
чтобы избежать этого.
debug()
Метод был реализован , чтобы запустить тест без сбора результата и отлов исключения.
TransactionTestCase
¶
-
класс
TransactionTestCase
¶
TransactionTestCase
наследуется от, SimpleTestCase
чтобы добавить некоторые особенности базы данных:
- Сброс базы данных в известное состояние в начале каждого теста, чтобы упростить тестирование и использование ORM.
- База данных
fixtures
. - Пропуск тестов на основе функций серверной части базы данных .
- Остальные специализированные
assert*
методы.
TestCase
Класс Django является более часто используемым подклассом,
TransactionTestCase
который использует средства транзакций базы данных для ускорения процесса сброса базы данных в известное состояние в начале каждого теста. Однако следствием этого является то, что некоторые виды поведения базы данных не могут быть протестированы в TestCase
классе Django . Например, вы не можете проверить, выполняется ли блок кода внутри транзакции, как это требуется при использовании
select_for_update()
. В этих случаях вам следует использовать TransactionTestCase
.
TransactionTestCase
и TestCase
идентичны, за исключением способа, которым база данных сбрасывается в известное состояние, и возможности тестового кода проверять эффекты фиксации и отката:
- A
TransactionTestCase
сбрасывает базу данных после запуска теста, усекая все таблицы. ATransactionTestCase
может вызывать фиксацию и откат и наблюдать за влиянием этих вызовов на базу данных. - С
TestCase
другой стороны, A не усекает таблицы после теста. Вместо этого он включает тестовый код в транзакцию базы данных, которая откатывается в конце теста. Это гарантирует, что откат в конце теста вернет базу данных в исходное состояние.
Предупреждение
TestCase
выполняется в базе данных, которая не поддерживает откат (например, MySQL с механизмом хранения MyISAM), и все экземпляры TransactionTestCase
будут откатываться в конце теста, удаляя все данные из тестовой базы данных.
Приложения не увидят перезагруженных данных ; если вам нужна эта функция (например, сторонние приложения должны ее включить), вы можете установить ее внутри
тела.serialized_rollback = True
TestCase
TestCase
¶
-
класс
TestCase
¶
Это наиболее распространенный класс для написания тестов в Django. Он наследуется от TransactionTestCase
(и по расширению SimpleTestCase
). Если ваше приложение Django не использует базу данных, используйте SimpleTestCase
.
Класс:
- Оборачивает тесты в два вложенных
atomic()
блока: один для всего класса и по одному для каждого теста. Поэтому, если вы хотите протестировать какое-то конкретное поведение транзакции базы данных, используйтеTransactionTestCase
. - Проверяет отложенные ограничения базы данных в конце каждого теста.
Он также предоставляет дополнительный метод:
-
classmethod
TestCase.
setUpTestData
() ¶ 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()
они будут вызываться перед каждым тестом, что отрицательно сказывается на скорости.Изменено в Django 3.2:Объекты, назначенные атрибутам класса в,
setUpTestData()
должны поддерживать создание глубоких копийcopy.deepcopy()
, чтобы изолировать их от изменений, выполняемых каждым методом тестирования. В предыдущих версиях Django эти объекты использовались повторно, и изменения, внесенные в них, сохранялись между методами тестирования.
-
classmethod
TestCase.
captureOnCommitCallbacks
( using = DEFAULT_DB_ALIAS , execute = False ) ¶ - Новое в Django 3.2.
Возвращает диспетчер контекста, который фиксирует
transaction.on_commit()
обратные вызовы для данного соединения с базой данных. Он возвращает список, содержащий при выходе из контекста захваченные функции обратного вызова. Из этого списка вы можете делать утверждения для обратных вызовов или вызывать их, чтобы вызвать их побочные эффекты, имитируя фиксацию.using
- это псевдоним подключения к базе данных, для которого требуется захватить обратные вызовы.Если
execute
естьTrue
, все обратные вызовы будут вызываться при выходе из диспетчера контекста, если не произошло исключения. Это имитирует фиксацию после обернутого блока кода.Например:
from django.core import mail from django.test import TestCase class ContactTests(TestCase): def test_post(self): with self.captureOnCommitCallbacks(execute=True) as callbacks: response = self.client.post( '/contact/', {'message': 'I like your site'}, ) self.assertEqual(response.status_code, 200) self.assertEqual(len(callbacks), 1) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].subject, 'Contact Form') self.assertEqual(mail.outbox[0].body, 'I like your site')
LiveServerTestCase
¶
-
класс
LiveServerTestCase
¶
LiveServerTestCase
делает в основном то же самое, что и
TransactionTestCase
с одной дополнительной функцией: он запускает живой сервер Django в фоновом режиме при установке и выключает его при разрыве. Это позволяет использовать автоматизированные тестовые клиенты, отличные от
фиктивного клиента Django, такие как, например,
клиент Selenium , для выполнения серии функциональных тестов внутри браузера и имитации действий реального пользователя.
Живой сервер прослушивает localhost
и связывается с портом 0, который использует свободный порт, назначенный операционной системой. Доступ к URL-адресу сервера можно получить
self.live_server_url
во время тестов.
Чтобы продемонстрировать, как пользоваться LiveServerTestCase
, напишем тест Selenium. Прежде всего, вам нужно установить пакет selenium на свой путь Python:
$ python -m pip install selenium
... \> py -m pip установить селен
Затем добавьте LiveServerTestCase
тест на основе -Base в модуль тестов вашего приложения (например:) 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().setUpClass()
cls.selenium = WebDriver()
cls.selenium.implicitly_wait(10)
@classmethod
def tearDownClass(cls):
cls.selenium.quit()
super().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
... \> manage.py test myapp.tests.MySeleniumTests.test_login
Этот пример автоматически откроет Firefox, затем перейдет на страницу входа, введите учетные данные и нажмите кнопку «Войти». Selenium предлагает другие драйверы, если у вас не установлен Firefox или вы хотите использовать другой браузер. Приведенный выше пример - это лишь малая часть того, что может делать клиент Selenium; ознакомьтесь с полной ссылкой для получения более подробной информации.
Примечание
При использовании базы данных SQLite в памяти для запуска тестов одно и то же соединение с базой данных будет совместно использоваться двумя потоками параллельно: потоком, в котором выполняется рабочий сервер, и потоком, в котором выполняется тестовый пример. Важно предотвратить одновременные запросы к базе данных через это общее соединение двумя потоками, поскольку это может иногда случайным образом приводить к сбою тестов. Поэтому вам нужно убедиться, что два потока не обращаются к базе данных одновременно. В частности, это означает, что в некоторых случаях (например, сразу после нажатия ссылки или отправки формы) вам может потребоваться проверить, что Selenium получил ответ и что следующая страница загружена, прежде чем продолжить дальнейшее выполнение теста. Сделайте это, например, заставив 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 FAQ и документации Selenium
для получения дополнительной информации.
Особенности тестовых случаев ¶
Тестовый клиент по умолчанию ¶
-
SimpleTestCase.
client
¶
Каждый тестовый пример в django.test.*TestCase
экземпляре имеет доступ к экземпляру тестового клиента Django. Доступ к этому клиенту можно получить как
self.client
. Этот клиент воссоздается для каждого теста, поэтому вам не нужно беспокоиться о переносе состояния (например, файлов cookie) из одного теста в другой.
Это означает, что вместо создания экземпляра a 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
атрибут class:
from django.test import Client, TestCase
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 test_fluffy_animals(self):
# A test that uses the fixtures.
call_some_test_code()
Вот что конкретно произойдет:
- В начале каждого теста, перед
setUp()
его запуском, Django очищает базу данных, возвращая базу данных в состояние, в котором она находилась сразу послеmigrate
вызова. - Затем устанавливаются все названные приспособления. В этом примере Django установит любое устройство JSON с именем
mammals
, за которым следует любое устройство с именемbirds
. См.loaddata
Документацию для получения более подробной информации об определении и установке приспособлений.
Из соображений производительности он TestCase
загружает фикстуры один раз для всего тестового класса, до setUpTestData()
, а не перед каждым тестом, и использует транзакции для очистки базы данных перед каждым тестом. В любом случае вы можете быть уверены, что на результат одного теста не повлияет другой тест или порядок его выполнения.
По умолчанию приборы загружаются только в default
базу данных. Если вы используете несколько баз данных и устанавливаете TransactionTestCase.databases
, фикстуры будут загружены во все указанные базы данных.
Конфигурация URLconf ¶
Если ваше приложение предоставляет представления, вы можете включить тесты, которые используют тестовый клиент для проверки этих представлений. Однако конечный пользователь может свободно развертывать представления в вашем приложении по любому URL-адресу по своему выбору. Это означает, что ваши тесты не могут полагаться на тот факт, что ваши представления будут доступны по определенному URL-адресу. Украсьте свой тестовый класс или тестовый метод с
@override_settings(ROOT_URLCONF=...)
помощью конфигурации URLconf.
Поддержка нескольких баз данных ¶
-
TransactionTestCase.
databases
¶
Django настраивает тестовую базу данных, соответствующую каждой базе данных, которая определена в DATABASES
определении в ваших настройках и на которую ссылается по крайней мере один тест databases
.
Однако большая часть времени, затрачиваемого на запуск Django TestCase
, уходит на вызов, flush
который гарантирует, что у вас будет чистая база данных в начале каждого тестового запуска. Если у вас несколько баз данных, требуется несколько сбросов (по одному для каждой базы данных), что может занять много времени, особенно если вашим тестам не нужно тестировать активность нескольких баз данных.
В целях оптимизации Django очищает default
базу данных только в начале каждого тестового запуска. Если ваша установка содержит несколько баз данных и у вас есть тест, требующий очистки каждой базы данных, вы можете использовать databases
атрибут в наборе тестов, чтобы запросить очистку дополнительных баз данных.
Например:
class TestMyViews(TransactionTestCase):
databases = {'default', 'other'}
def test_index_page_view(self):
call_some_test_code()
Этот тестовый пример перед запуском очистит базы данных default
и other
test test_index_page_view
. Вы также можете использовать, '__all__'
чтобы указать, что все тестовые базы данных должны быть очищены.
databases
Флаг также управляет базами данных , которые
TransactionTestCase.fixtures
загружаются в. По умолчанию приборы загружаются только в default
базу данных.
Запросы к базам данных, не входящим в состав, databases
будут давать ошибки утверждения, чтобы предотвратить утечку состояния между тестами.
-
TestCase.
databases
¶
По умолчанию только default
база данных будет заключена в транзакцию во время TestCase
выполнения, и попытки запроса других баз данных приведут к ошибкам утверждения, чтобы предотвратить утечку состояния между тестами.
Используйте databases
атрибут class в тестовом классе, чтобы запросить перенос транзакции для не default
баз данных.
Например:
class OtherDBTests(TestCase):
databases = {'other'}
def test_other_db_query(self):
...
Этот тест разрешит запросы только к other
базе данных. Как и для
SimpleTestCase.databases
и TransactionTestCase.databases
,
'__all__'
константа может использоваться, чтобы указать, что тест должен разрешать запросы ко всем базам данных.
Переопределение настроек ¶
Предупреждение
Используйте указанные ниже функции, чтобы временно изменить значение настроек в тестах. Не выполняйте манипуляции django.conf.settings
напрямую, так как Django не восстановит исходные значения после таких манипуляций.
-
SimpleTestCase.
settings
() ¶
В целях тестирования часто бывает полезно временно изменить параметр и вернуться к исходному значению после запуска кода тестирования. Для этого варианта использования 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
() ¶
Переопределение параметров, содержащих список значений, может оказаться громоздким. На практике часто бывает достаточно добавления или удаления значений. Django предоставляет
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
() ¶
Если вы хотите переопределить настройку для метода тестирования, 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
() ¶
Точно так же 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 | Часовой пояс баз данных |
ШАБЛОНЫ | Шаблонные движки |
SERIALIZATION_MODULES | Кеш сериализаторов |
LOCALE_PATHS, LANGUAGE_CODE | Перевод по умолчанию и загруженные переводы |
MEDIA_ROOT, DEFAULT_FILE_STORAGE | Хранилище файлов по умолчанию |
Очистка тестового почтового ящика ¶
Если вы используете какой-либо из настраиваемых TestCase
классов Django, средство запуска тестов очищает содержимое тестового почтового ящика в начале каждого тестового примера.
Дополнительные сведения о службах электронной почты во время тестов см. В разделе Службы электронной почты ниже.
Утверждения ¶
Поскольку обычный unittest.TestCase
класс Python реализует такие методы утверждения, как assertTrue()
и
assertEqual()
, пользовательский TestCase
класс Django предоставляет ряд пользовательских методов утверждения, которые полезны для тестирования веб-приложений:
Сообщения об ошибках, выдаваемые большинством этих методов утверждения, можно настроить с помощью msg_prefix
аргумента. Эта строка будет добавляться к любому сообщению об ошибке, сгенерированному утверждением. Это позволяет вам предоставить дополнительные сведения, которые могут помочь вам определить местоположение и причину сбоя в вашем наборе тестов.
-
SimpleTestCase.
assertRaisesMessage
( Expected_exception , expected_message , вызываемые , * Args , ** kwargs ) ¶ -
SimpleTestCase.
assertRaisesMessage
( ожидаемое_исключение , ожидаемое_сообщение ) Утверждает , что выполнение
callable
подъемовexpected_exception
иexpected_message
находится в сообщении , за исключением в. Любой другой результат считается неудачным. Это более простая версияunittest.TestCase.assertRaisesRegex()
с той разницей, чтоexpected_message
она не рассматривается как регулярное выражение.Если только
expected_exception
иexpected_message
параметры заданы, возвращает менеджер контекста , так что код испытывается можно записать рядный , а не как функции:with self.assertRaisesMessage(ValueError, 'invalid literal for int()'): int('a')
-
SimpleTestCase.
assertWarnsMessage
( ожидаемое_предупреждение , ожидаемое_сообщение , вызываемое , * аргументы , ** kwargs ) ¶ -
SimpleTestCase.
assertWarnsMessage
( ожидаемое_предупреждение , ожидаемое_сообщение ) Аналогично,
SimpleTestCase.assertRaisesMessage()
но дляassertWarnsRegex()
вместоassertRaisesRegex()
.
-
SimpleTestCase.
assertFieldOutput
( fieldclass , действительный , недопустимый , field_args = None , field_kwargs = None , empty_value = '' ) ¶ Утверждает, что поле формы правильно работает с различными входными данными.
Параметры: - fieldclass - класс тестируемого поля.
- valid - словарь, отображающий допустимые входные данные на их ожидаемые очищенные значения.
- invalid - словарь отображает недопустимые входные данные в одно или несколько сообщений об ошибках.
- field_args - аргументы, переданные для создания экземпляра поля.
- field_kwargs - kwargs, переданные для создания экземпляра поля.
- empty_value - ожидаемый чистый вывод для входов в
empty_values
.
Например, следующий код проверяет,
EmailField
принимает ли[email protected]
объект как действительный адрес электронной почты, но отклоняетaaa
с разумным сообщением об ошибке:self.assertFieldOutput(EmailField, {'[email protected]': '[email protected]'}, {'aaa': ['Enter a valid email address.']})
-
SimpleTestCase.
assertFormError
( ответ , форма , поле , ошибки , msg_prefix = '' ) ¶ Утверждает, что поле в форме вызывает указанный список ошибок при отображении в форме.
form
- это имя, данноеForm
экземпляру в контексте шаблона.field
имя поля формы для проверки. Еслиfield
имеет значениеNone
,form.non_field_errors()
будут проверяться неполевые ошибки (ошибки, к которым можно получить доступ через ).errors
- строка ошибки или список строк ошибок, ожидаемых в результате проверки формы.
-
SimpleTestCase.
assertFormsetError
( ответ , набор форм , индекс_форм , поле , ошибки , msg_prefix = '' ) ¶ Утверждает, что
formset
при визуализации вызывает указанный список ошибок.formset
- это имя, данноеFormset
экземпляру в контексте шаблона.form_index
номер формы внутриFormset
. Еслиform_index
имеет значениеNone
,formset.non_form_errors()
будут проверяться ошибки, не связанные с формой (ошибки, к которым можно получить доступ через ).field
имя поля формы для проверки. Еслиfield
имеет значениеNone
,form.non_field_errors()
будут проверяться неполевые ошибки (ошибки, к которым можно получить доступ через ).errors
- строка ошибки или список строк ошибок, ожидаемых в результате проверки формы.
-
SimpleTestCase.
assertContains
( ответ , текст , count = None , status_code = 200 , msg_prefix = '' , html = False ) ¶ Утверждает, что
Response
экземпляр произвел данноеstatus_code
и чтоtext
появляется в содержании ответа. Еслиcount
предоставляется, в ответеtext
должно встречаться ровноcount
раз.Установите
html
дляTrue
обработкиtext
как HTML. Сравнение с содержимым ответа будет основано на семантике HTML, а не на посимвольном равенстве. В большинстве случаев пробелы игнорируются, порядок атрибутов не имеет значения. ПодробнееassertHTMLEqual()
см.
-
SimpleTestCase.
assertNotContains
( ответ , текст , status_code = 200 , msg_prefix = '' , html = False ) ¶ Утверждает, что
Response
экземпляр произвел данное,status_code
аtext
это не отображается в содержимом ответа.Установите
html
дляTrue
обработкиtext
как HTML. Сравнение с содержимым ответа будет основано на семантике HTML, а не на посимвольном равенстве. В большинстве случаев пробелы игнорируются, порядок атрибутов не имеет значения. ПодробнееassertHTMLEqual()
см.
-
SimpleTestCase.
assertTemplateUsed
( Ответ , TEMPLATE_NAME , msg_prefix = '' , не считать = None ) ¶ Утверждает, что шаблон с заданным именем использовался при рендеринге ответа.
Имя представляет собой строку, например
'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
( Ответ , TEMPLATE_NAME , msg_prefix = '' ) ¶ Утверждает, что шаблон с данным именем не использовался при рендеринге ответа.
Вы можете использовать это как менеджер контекста точно так же, как
assertTemplateUsed()
.
-
SimpleTestCase.
assertURLEqual
( url1 , url2 , msg_prefix = '' ) ¶ Утверждает, что два URL-адреса одинаковы, игнорируя порядок параметров строки запроса, за исключением параметров с тем же именем. Например,
/path/?x=1&y=2
равно/path/?y=2&x=1
, но/path/?a=1&a=2
не равно/path/?a=2&a=1
.
-
SimpleTestCase.
assertRedirects
( ответ , ожидаемый_url , status_code = 302 , target_status_code = 200 , msg_prefix = '' , fetch_redirect_response = True ) ¶ Утверждает, что ответ вернул
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
используется для сравнения.
-
SimpleTestCase.
assertHTMLEqual
( html1 , html2 , msg = None ) ¶ Утверждает, что строки
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 ) ¶ Утверждает , что строки
html1
иhtml2
являются не равны. Сравнение основано на семантике HTML. См.assertHTMLEqual()
Подробности.html1
иhtml2
должен содержать HTML.AssertionError
Будет поднят , если один из них не может быть разобран.Вывод в случае ошибки можно настроить с помощью
msg
аргумента.
-
SimpleTestCase.
assertXMLEqual
( xml1 , xml2 , msg = Нет ) ¶ Утверждает, что строки
xml1
иxml2
равны. Сравнение основано на семантике XML. АналогичноassertHTMLEqual()
, сравнение выполняется на проанализированном контенте, поэтому учитываются только семантические различия, а не синтаксические различия. Когда в каком-либо параметре передается недопустимый XML,AssertionError
всегда вызывается, даже если обе строки идентичны.Объявление XML, тип документа, инструкции по обработке и комментарии игнорируются. Сравниваются только корневой элемент и его дочерние элементы.
Вывод в случае ошибки можно настроить с помощью
msg
аргумента.
-
SimpleTestCase.
assertXMLNotEqual
( xml1 , xml2 , msg = Нет ) ¶ Утверждает , что строки
xml1
иxml2
являются не равны. Сравнение основано на семантике XML. См.assertXMLEqual()
Подробности.Вывод в случае ошибки можно настроить с помощью
msg
аргумента.
-
SimpleTestCase.
assertInHTML
( иголка , стог сена , count = None , msg_prefix = '' ) ¶ Утверждает, что фрагмент HTML
needle
содержится вhaystack
одном.Если
count
указан целочисленный аргумент, то дополнительноneedle
будет строго проверяться количество вхождений.Пробелы в большинстве случаев игнорируются, а порядок атрибутов не имеет значения. Подробнее
assertHTMLEqual()
см.
-
SimpleTestCase.
assertJSONEqual
( необработанные , ожидаемые_данные , сообщение = Нет ) ¶ Утверждает, что фрагменты JSON
raw
иexpected_data
равны. Обычные правила для незначащих пробелов JSON применяются, поскольку тяжелый вес делегируетсяjson
библиотеке.Вывод в случае ошибки можно настроить с помощью
msg
аргумента.
-
SimpleTestCase.
assertJSONNotEqual
( необработанные , ожидаемые_данные , сообщение = Нет ) ¶ Утверждает , что фрагменты JSON
raw
иexpected_data
являются не равны. ПодробнееassertJSONEqual()
см.Вывод в случае ошибки можно настроить с помощью
msg
аргумента.
-
TransactionTestCase.
assertQuerysetEqual
( qs , значения , преобразование = Нет , упорядочено = Истина , сообщение = Нет ) ¶ Утверждает, что набор запросов
qs
соответствует определенной итерации значенийvalues
.Если
transform
предоставляется,values
сравнивается со списком, созданным путем примененияtransform
к каждому членуqs
.По умолчанию сравнение также зависит от порядка. Если
qs
не обеспечивает неявное упорядочение, вы можете установить дляordered
параметра значениеFalse
, которое превращает сравнение вcollections.Counter
сравнение. Если порядок не определен (если данныйqs
не упорядочен и сравнение проводится с более чем одним упорядоченным значением), возникает aValueError
.Вывод в случае ошибки можно настроить с помощью
msg
аргумента.Изменено в Django 3.2:Значение
transform
аргумента по умолчанию было изменено наNone
.Новое в Django 3.2:Добавлена поддержка прямого сравнения между наборами запросов.
Не рекомендуется с версии 3.2: Если
transform
не указан иvalues
представляет собой список строк, он сравнивается со списком, созданным путем примененияrepr()
к каждому членуqs
. Такое поведение устарело и будет удалено в Django 4.1. Если вам это нужно, явно установитеtransform
значениеrepr
.
-
TransactionTestCase.
assertNumQueries
( число , функция , * аргументы , ** kwargs ) ¶ Утверждает, что when
func
вызывается with*args
и**kwargs
чтоnum
запросы к базе данных выполняются.Если в нем
"using"
присутствует ключ,kwargs
он используется как псевдоним базы данных, для которого проверяется количество запросов:self.assertNumQueries(7, using='non_default_db')
Если вы хотите вызвать функцию с
using
параметром, вы можете сделать это, заключив вызов в a,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):
...
Подклассы наследуют теги от суперклассов, а методы наследуют теги от своего класса. Дано:
@tag('foo')
class SampleTestCaseChild(SampleTestCase):
@tag('bar')
def test(self):
...
SampleTestCaseChild.test
будут помечены 'slow'
, 'core'
,
'bar'
и 'foo'
.
Затем вы можете выбрать, какие тесты запускать. Например, чтобы запускать только быстрые тесты:
$ ./manage.py test --tag=fast
...\> manage.py test --tag=fast
Или запустить быстрые тесты и основной (хоть и медленный):
$ ./manage.py test --tag=fast --tag=core
...\> manage.py test --tag=fast --tag=core
Вы также можете исключить тесты по тегу. Чтобы запустить основные тесты, если они не медленные:
$ ./manage.py test --tag=core --exclude-tag=slow
...\> manage.py test --tag=core --exclude-tag=slow
test --exclude-tag
имеет приоритет , поэтому, если в тесте есть два тега, и вы выбираете один из них и исключаете другой, тест не будет запущен.test --tag
Тестирование асинхронного кода ¶
Если вы просто хотите протестировать вывод своих асинхронных представлений, стандартный тестовый клиент запустит их в собственном асинхронном цикле без какой-либо дополнительной работы с вашей стороны.
Однако, если вы хотите написать полностью асинхронные тесты для проекта Django, вам нужно будет учесть несколько вещей.
Во-первых, ваши тесты должны быть методами в тестовом классе (чтобы дать им асинхронный контекст). Django автоматически обнаруживает любые тесты и обертывает их, чтобы они запускались в собственном цикле событий.async def
async def
Если вы тестируете из асинхронной функции, вы также должны использовать клиент асинхронного тестирования. Это доступно как django.test.AsyncClient
или как self.async_client
на любом тесте.
AsyncClient
имеет те же методы и подписи, что и синхронный (нормальный) тестовый клиент, за двумя исключениями:
follow
Параметр не поддерживается.Заголовки, передаваемые как
extra
аргументы ключевого слова, не должны иметьHTTP_
префикса, требуемого синхронным клиентом (смClient.get()
. Раздел "Ресурсы" ). Например, вот как установитьAccept
заголовок HTTP :>>> c = AsyncClient() >>> c.get( ... '/customers/details/', ... {'name': 'fred', 'age': 7}, ... ACCEPT='application/json' ... )
При использовании AsyncClient
любого метода, который делает запрос, необходимо дождаться:
async def test_my_thing(self):
response = await self.async_client.get('/some-url/')
self.assertEqual(response.status_code, 200)
Асинхронный клиент также может вызывать синхронные представления; он проходит через асинхронный путь запроса Django , который поддерживает оба. Любое представление, вызываемое через AsyncClient
, получит ASGIRequest
объект для своего, request
а не для того, WSGIRequest
что создает обычный клиент.
Предупреждение
Если вы используете тестовые декораторы, они должны быть асинхронными, чтобы гарантировать правильную работу. Встроенные декораторы Django будут вести себя правильно, но сторонние декораторы могут не работать (они «обернут» неправильную часть потока выполнения, а не ваш тест).
Если вам нужно использовать эти декораторы, вы должны вместо этого украсить свои тестовые методы внутри них:async_to_sync()
from asgiref.sync import async_to_sync
from django.test import TestCase
class MyTests(TestCase):
@mock.patch(...)
@async_to_sync
async def test_my_thing(self):
...
Почтовые службы ¶
Если какое-либо из ваших представлений 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.',
'[email protected]', ['[email protected]'],
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 io import StringIO
from django.core.management import call_command
from django.test import TestCase
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 ) ¶
Пропустите оформленный тест или, TestCase
если поддерживаются все названные функции базы данных.
Например, следующий тест не будет выполняться, если база данных поддерживает транзакции (например, он не будет работать под PostgreSQL, но он будет работать под MySQL с таблицами MyISAM):
class MyTests(TestCase):
@skipIfDBFeature('supports_transactions')
def test_transaction_behavior(self):
# ... conditional test code
pass
-
skipUnlessDBFeature
( * feature_name_strings ) ¶
Пропустите оформленный тест или TestCase
если какие-либо из названных функций базы данных не поддерживаются.
Например, следующий тест будет выполняться только в том случае, если база данных поддерживает транзакции (например, он будет работать под PostgreSQL, но не под MySQL с таблицами MyISAM):
class MyTests(TestCase):
@skipUnlessDBFeature('supports_transactions')
def test_transaction_behavior(self):
# ... conditional test code
pass