Test django
Руководство часть 10: Тестирование приложений Django - Изучение веб-разработки | MDN
Простые тесты
from django.test import TestCase
from django.urls import reverse
class WelcomePageTest(TestCase):
def test_welcome_page(self):
url = reverse('welcome')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Добро пожаловать!")
Проверить использование шаблона: assertTemplateUsed(response, ‘goods/items_list.html’)
Загрузка тестовых данных в таблицу: setUpTestData
class ItemsTest(TestCase):
@classmethod
def setUpTestData(cls):
for item_index in range(NUMBER_OF_ITEMS):
Item.objects.create(code=f'code {item_index}', price=Decimal(randint(1,100)))
Структура тестов
Джанго поддерживает встроенный обнаружитель тестов, который находит тесты в текущей рабочей директории, в любом файле с шаблонным именем test*.py.
Рекомендуется создавать пакет:
catalog/
/tests/
__init__.py
test_models.py
test_forms.py
test_views.py
from django.test import TestCase
class YourTestClass(TestCase):
@classmethod
def setUpTestData(cls):
print("setUpTestData: Run once to set up non-modified data for all class methods.")
pass
def setUp(self):
print("setUp: Run once for every test method to setup clean data.")
pass
def test_false_is_false(self):
print("Method: test_false_is_false.")
self.assertFalse(False)
def test_false_is_true(self):
print("Method: test_false_is_true.")
self.assertTrue(False)
def test_one_plus_one_equals_two(self):
print("Method: test_one_plus_one_equals_two.")
self.assertEqual(1 + 1, 2)
setUpTestData - вызывается каждый раз перед запуском теста на уровне настройки всего класса. Мы должны использовать этот методы для создания объектов, которые не будут изменяться в каком-либо из тестовых методов
setUp - вызывается перед каждой тестовой функцией для настройки объектов, которые могут изменяться во время тестов (каждая функция тестирования будет получать “свежую” версию данных объектов).
Запуск тестов
py manage.py test
python3 manage.py test catalog.tests # Run the specified module python3 manage.py test catalog.tests.test_models # Run the specified module python3 manage.py test catalog.tests.test_models.YourTestClass # Run the specified class python3 manage.py test catalog.tests.test_models.YourTestClass.test_one_plus_one_equals_two # Run the specified method
Написание тестов
# Получение объекта для тестирования
author=Author.objects.get(id=1)
# Получение метаданных поля для получения необходимых значений
field_label = author._meta.get_field('first_name').verbose_name
# Сравнить значение с ожидаемым результатом
self.assertEquals(field_label,'first name')
Проверка валидаторов, текстов с подсказками и т.д.
from django.test import TestCase
# Создайте ваши тесты здесь
import datetime
from django.utils import timezone
from catalog.forms import RenewBookForm
class RenewBookFormTest(TestCase):
def test_renew_form_date_field_label(self):
form = RenewBookForm()
self.assertTrue(form.fields['renewal_date'].label == None or form.fields['renewal_date'].label == 'renewal date')
def test_renew_form_date_field_help_text(self):
form = RenewBookForm()
self.assertEqual(form.fields['renewal_date'].help_text,'Enter a date between now and 4 weeks (default 3).')
def test_renew_form_date_in_past(self):
date = datetime.date.today() - datetime.timedelta(days=1)
form_data = {'renewal_date': date}
form = RenewBookForm(data=form_data)
self.assertFalse(form.is_valid())
def test_renew_form_date_too_far_in_future(self):
date = datetime.date.today() + datetime.timedelta(weeks=4) + datetime.timedelta(days=1)
form_data = {'renewal_date': date}
form = RenewBookForm(data=form_data)
self.assertFalse(form.is_valid())
def test_renew_form_date_today(self):
date = datetime.date.today()
form_data = {'renewal_date': date}
form = RenewBookForm(data=form_data)
self.assertTrue(form.is_valid())
def test_renew_form_date_max(self):
date = timezone.now() + datetime.timedelta(weeks=4)
form_data = {'renewal_date': date}
form = RenewBookForm(data=form_data)
self.assertTrue(form.is_valid())
Отображения/представления/вьюхи
Общее
Используется клиент джанго
Попробуем написать тест, который проверяет возвращает ли джанго всех авторов блоком по 10.
class AuthorListView(generic.ListView):
model = Author
paginate_by = 10
from django.test import TestCase
# Create your tests here.
from catalog.models import Author
from django.urls import reverse
class AuthorListViewTest(TestCase):
@classmethod
def setUpTestData(cls):
#Create 13 authors for pagination tests
number_of_authors = 13
for author_num in range(number_of_authors):
Author.objects.create(first_name='Christian %s' % author_num, last_name = 'Surname %s' % author_num,)
def test_view_url_exists_at_desired_location(self):
resp = self.client.get('/catalog/authors/')
self.assertEqual(resp.status_code, 200)
def test_view_url_accessible_by_name(self):
resp = self.client.get(reverse('authors'))
self.assertEqual(resp.status_code, 200)
def test_view_uses_correct_template(self):
resp = self.client.get(reverse('authors'))
self.assertEqual(resp.status_code, 200)
self.assertTemplateUsed(resp, 'catalog/author_list.html')
def test_pagination_is_ten(self):
resp = self.client.get(reverse('authors'))
self.assertEqual(resp.status_code, 200)
self.assertTrue('is_paginated' in resp.context)
self.assertTrue(resp.context['is_paginated'] == True)
self.assertTrue( len(resp.context['author_list']) == 10)
def test_lists_all_authors(self):
#Get second page and confirm it has (exactly) remaining 3 items
resp = self.client.get(reverse('authors')+'?page=2')
self.assertEqual(resp.status_code, 200)
self.assertTrue('is_paginated' in resp.context)
self.assertTrue(resp.context['is_paginated'] == True)
self.assertTrue( len(resp.context['author_list']) == 3)
- в
setUpTestDataмы создаем 13 автором - в
test_view_url_exists_at_desired_locationмы проверяем доступность УРЛа - в
test_view_url_accessible_by_nameмы проверяем доступность УРЛа поnamespace - в
test_view_uses_correct_templateпроверяем правильный ли шаблон используется - в
test_pagination_is_tenпроверяем пагинацию
Отображение и регистрация пользователей
login = self.client.login(username='testuser1', password='12345')
import datetime
from django.utils import timezone
from catalog.models import BookInstance, Book, Genre, Language
from django.contrib.auth.models import User # Необходимо для представления User как borrower
class LoanedBookInstancesByUserListViewTest(TestCase):
def setUp(self):
# Создание двух пользователей
test_user1 = User.objects.create_user(username='testuser1', password='12345')
test_user1.save()
test_user2 = User.objects.create_user(username='testuser2', password='12345')
test_user2.save()
# Создание книги
test_author = Author.objects.create(first_name='John', last_name='Smith')
test_genre = Genre.objects.create(name='Fantasy')
test_language = Language.objects.create(name='English')
test_book = Book.objects.create(title='Book Title', summary = 'My book summary', isbn='ABCDEFG', author=test_author, language=test_language)
# Create genre as a post-step
genre_objects_for_book = Genre.objects.all()
test_book.genre.set(genre_objects_for_book) # Присвоение типов many-to-many напрямую недопустимо
test_book.save()
# Создание 30 объектов BookInstance
number_of_book_copies = 30
for book_copy in range(number_of_book_copies):
return_date= timezone.now() + datetime.timedelta(days=book_copy%5)
if book_copy % 2:
the_borrower=test_user1
else:
the_borrower=test_user2
status='m'
BookInstance.objects.create(book=test_book,imprint='Unlikely Imprint, 2016', due_back=return_date, borrower=the_borrower, status=status)
def test_redirect_if_not_logged_in(self):
resp = self.client.get(reverse('my-borrowed'))
self.assertRedirects(resp, '/accounts/login/?next=/catalog/mybooks/')
def test_logged_in_uses_correct_template(self):
login = self.client.login(username='testuser1', password='12345')
resp = self.client.get(reverse('my-borrowed'))
# Проверка что пользователь залогинился
self.assertEqual(str(resp.context['user']), 'testuser1')
# Проверка ответа на запрос
self.assertEqual(resp.status_code, 200)
# Проверка того, что мы используем правильный шаблон
self.assertTemplateUsed(resp, 'catalog/bookinstance_list_borrowed_user.html')
Важно, что при созданни экземпляра пользователя используется метод create_user. save() потом применять не обязательно, по крайней мере у меня уже такой код проходит
Проверка что пользователь использует только предназначенные ему ресурсы
def test_only_borrowed_books_in_list(self):
login = self.client.login(username='testuser1', password='12345')
resp = self.client.get(reverse('my-borrowed'))
#Проверка, что пользователь залогинился
self.assertEqual(str(resp.context['user']), 'testuser1')
#Check that we got a response "success"
self.assertEqual(resp.status_code, 200)
#Проверка, что изначально у нас нет книг в списке
self.assertTrue('bookinstance_list' in resp.context)
self.assertEqual( len(resp.context['bookinstance_list']),0)
#Теперь все книги "взяты на прокат"
get_ten_books = BookInstance.objects.all()[:10]
for copy in get_ten_books:
copy.status='o'
copy.save()
#Проверка, что все забронированные книги в списке
resp = self.client.get(reverse('my-borrowed'))
#Проверка, что пользователь залогинился
self.assertEqual(str(resp.context['user']), 'testuser1')
#Проверка успешности ответа
self.assertEqual(resp.status_code, 200)
self.assertTrue('bookinstance_list' in resp.context)
#Подтверждение, что все книги принадлежат testuser1 и взяты "на прокат"
for bookitem in resp.context['bookinstance_list']:
self.assertEqual(resp.context['user'], bookitem.borrower)
self.assertEqual('o', bookitem.status)
def test_pages_ordered_by_due_date(self):
#Изменение статуса на "в прокате"
for copy in BookInstance.objects.all():
copy.status='o'
copy.save()
login = self.client.login(username='testuser1', password='12345')
resp = self.client.get(reverse('my-borrowed'))
#Пользователь залогинился
self.assertEqual(str(resp.context['user']), 'testuser1')
#Check that we got a response "success"
self.assertEqual(resp.status_code, 200)
#Подтверждение, что из всего списка показывается только 10 экземпляров
self.assertEqual( len(resp.context['bookinstance_list']),10)
last_date=0
for copy in resp.context['bookinstance_list']:
if last_date==0:
last_date=copy.due_back
else:
self.assertTrue(last_date <= copy.due_back)