Unittest в Django: тестирование Views

В приложении deals есть четыре view-класса.

Нужно удостовериться, что в каждом из этих классов:

  • при обращении к определённому имени (указанному в path() в аргументе name) для отображения страниц вызывается ожидаемый HTML-шаблон;
  • в шаблон передан правильный контекст.

Тесты для views сохранены в директории deals/tests.

└── deals
    ├── __init__.py
    ├── tests
    │   ├── __init__.py  
    │   ├── test_forms.py   
    │   ├── test_models.py      
    │   ├── test_views.py   # Тесты отображений 
    │   └── test_urls.py     
    ├── admin.py
    ├── forms.py
    ├── models.py
    └── views.py 

Проверка 1: view-классы используют ожидаемые HTML-шаблоны

Обратиться из кода к адресам приложения по имени name можно через метод reverse():

self.client.get(reverse('имя_приложения:name')) 

Для тестирования страницы deals:task_detail нужно создать запись в базе данных. Все остальные страницы проекта можно тестировать без предварительной подготовки.Теперь берём все страницы по очереди и тестируем. Чтобы проверить, какой шаблон использует view-класс или view-функция, в django.test есть утверждение assertTemplateUsed:

# deals/tests/test_views.py
from django.contrib.auth import get_user_model
from django.test import Client, TestCase
from django.urls import reverse

from deals.models import Task

User = get_user_model()


class TaskPagesTests(TestCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        # Создадим запись в БД, 
        # она понадобится для тестирования страницы deals:task_detail
        Task.objects.create(
            title='Заголовок',
            text='Текст',
            slug='test-slug',
        )

    def setUp(self):
        # Создаем авторизованный клиент
        self.user = User.objects.create_user(username='StasBasov')
        self.authorized_client = Client()
        self.authorized_client.force_login(self.user)

    def test_about_page_uses_correct_template(self):
        """URL-адрес использует шаблон deals/home.html."""
        response = self.authorized_client.get(reverse('deals:task_added'))
        self.assertTemplateUsed(response, 'deals/added.html')

    def test_home_page_correct_template(self):
        """URL-адрес использует шаблон deals/home.html."""
        response = self.authorized_client.get(reverse('deals:home'))
        self.assertTemplateUsed(response, 'deals/home.html')

    def test_task_list_page_authorized_uses_correct_template(self):
        """URL-адрес использует шаблон deals/task_list.html."""
        response = self.authorized_client.get(reverse('deals:task_list'))
        self.assertTemplateUsed(response, 'deals/task_list.html')

    def test_task_detail_pages_authorized_uses_correct_template(self):
        """URL-адреса используют шаблон deals/task_detail.html."""
        response = self.authorized_client.\
            get(reverse('deals:task_detail', kwargs={'slug': 'test-slug'}))
        self.assertTemplateUsed(response, 'deals/task_detail.html') 

Четыре теста проверяют код по одинаковому алгоритму. Разумеется, такие тесты можно написать лаконичнее, через subTest:

# deals/tests/test_views.py
from django.contrib.auth import get_user_model
from django.test import Client, TestCase
from django.urls import reverse

from deals.models import Task

User = get_user_model()


class TaskPagesTests(TestCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        # Создадим запись в БД
        Task.objects.create(
            title='Заголовок',
            text='Текст',
            slug='test-slug',
        )

    def setUp(self):
        # Создаем авторизованный клиент
        self.user = User.objects.create_user(username='StasBasov')
        self.authorized_client = Client()
        self.authorized_client.force_login(self.user)

    # Проверяем используемые шаблоны
    def test_pages_uses_correct_template(self):
        """URL-адрес использует соответствующий шаблон."""
        # Собираем в словарь пары "имя_html_шаблона: reverse(name)"
        templates_pages_names = {
            'deals/home.html': reverse('deals:home'),
            'deals/added.html': reverse('deals:task_added'),
            'deals/task_list.html': reverse('deals:task_list'),
            'deals/task_detail.html': (
                reverse('deals:task_detail', kwargs={'slug': 'test-slug'})
            ),
        }
        # Проверяем, что при обращении к name вызывается соответствующий HTML-шаблон
        for template, reverse_name in templates_pages_names.items():
            with self.subTest(reverse_name=reverse_name):
                response = self.authorized_client.get(reverse_name)
                self.assertTemplateUsed(response, template) 

Проверка 2: в шаблон передан правильный контекст

При создании страницы в неё передаётся словарь с контекстом. При обращении к странице можно получить этот словарь из свойства context объекта response, после чего проверить содержимое полей словаря response.context на совпадение с ожидаемым результатом.

# deals/tests/test_views.py
from django.contrib.auth import get_user_model
from django.test import Client, TestCase
from django.urls import reverse
from django import forms  

from deals.models import Task

User = get_user_model()


class TaskPagesTests(TestCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        # Создадим запись в БД
        Task.objects.create(
            title='Заголовок',
            text='Текст',
            slug='test-slug',
        )

    def setUp(self):
        # Создаем авторизованный клиент
        self.user = User.objects.create_user(username='StasBasov')
        self.authorized_client = Client()
        self.authorized_client.force_login(self.user)

    # Проверка словаря контекста главной страницы (в нём передаётся форма)
    def test_home_page_show_correct_context(self):
        """Шаблон home сформирован с правильным контекстом."""
        response = self.authorized_client.get(reverse('deals:home'))
        # Словарь ожидаемых типов полей формы:
        # указываем, объектами какого класса должны быть поля формы
        form_fields = {
            'title': forms.fields.CharField,
            # При создании формы поля модели типа TextField 
            # преобразуются в CharField с виджетом forms.Textarea           
            'text': forms.fields.CharField,
            'slug': forms.fields.SlugField,
            'image': forms.fields.ImageField,
        }        

        # Проверяем, что типы полей формы в словаре context соответствуют ожиданиям
        for value, expected in form_fields.items():
            with self.subTest(value=value):
                form_field = response.context.get('form').fields.get(value)
                # Проверяет, что поле формы является экземпляром
                # указанного класса
                self.assertIsInstance(form_field, expected)

    # Проверяем, что словарь context страницы /task
    # в первом элементе списка object_list содержит ожидаемые значения 
    def test_task_list_page_show_correct_context(self):
        """Шаблон task_list сформирован с правильным контекстом."""
        response = self.authorized_client.get(reverse('deals:task_list'))
        # Взяли первый элемент из списка и проверили, что его содержание
        # совпадает с ожидаемым
        first_object = response.context['object_list'][0]
        task_title_0 = first_object.title
        task_text_0 = first_object.text
        task_slug_0 = first_object.slug
        self.assertEqual(task_title_0, 'Заголовок')
        self.assertEqual(task_text_0, 'Текст')
        self.assertEqual(task_slug_0, 'test-slug')


    # Проверяем, что словарь context страницы task/test-slug
    # содержит ожидаемые значения 
    def test_task_detail_pages_show_correct_context(self):
        """Шаблон task_detail сформирован с правильным контекстом."""
        response = (self.authorized_client.
            get(reverse('deals:task_detail', kwargs={'slug': 'test-slug'})))
        self.assertEqual(response.context.get('task').title, 'Заголовок')
        self.assertEqual(response.context.get('task').text, 'Текст')
        self.assertEqual(response.context.get('task').slug, 'test-slug') 

Тестирование staticpages

В проекте Todo у приложения staticpages есть лишь одна страница: /page/about/. Тестирование URL и views этой страницы отличается от тестов приложения deals только простотой. Тесты приложения есть в проекте. Посмотрите, вам понравится.

Тестирование паджинатора

Для тестирования паджинатора Yatube можно создать в фикстурах несколько объектов Post, а затем проверить, сколько записей передаётся на страницу в словаре context. Объектов в фикстурах должно быть больше, чем выводится на одну страницу паджинатора.

# Здесь импорт необходимых библиотек для тестов.
...
class PaginatorViewsTest(TestCase):
    # Здесь создаются фикстуры: клиент и 13 тестовых записей.
    ...
    def test_first_page_contains_ten_records(self):
        response = self.client.get(reverse('index'))
        # Проверка: количество постов на первой странице равно 10. 
        self.assertEqual(len(response.context['object_list']), 10)

    def test_second_page_contains_three_records(self):
        # Проверка: на второй странице должно быть три поста.
        response = self.client.get(reverse('index') + '?page=2')
        self.assertEqual(len(response.context['object_list']), 3) 

Дополнительно можно проверить, что содержимое постов на странице соответствует ожиданиям — подобную проверку вы проводили, тестируя views.

Получить содержимое страницы поможет запрос response.context.get('page').object_list

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: