Продвинутый Django 5 для продолжающих

Прогресс по курсу:  0/193

4.6 Написание тестов для представлений
3 из 4 шагов пройдено
0 из 3 баллов  получено

В этом шаге мы добавим код для просмотра домашней страницы. Для этого нам необходимо выполнить следующие шаги:

  • Нам нужно отправить данные, полученные из формы, в базу данных, если входные данные формы действительны.
  • Нам нужен способ представить данные формы на главной странице.
  • Нам нужно отобразить пустую форму, чтобы пользователи могли ввести новую запись данных. Это было реализовано раньше, поэтому мы рассмотрим первые два.

Представления:

Добавим данный код для наших для представлений в файл views.py:

from django.shortcuts import render
from .forms import AddBookForm
from .models import Catalog


def home(request):
    if request.method == 'POST':
        add_book_form = AddBookForm(data=request.POST)
        if add_book_form.is_valid():
            add_book_form.save()
    books = Catalog.objects.all()
    add_book_form = AddBookForm()

    return render(request, 'home.html', {
        "add_book_form": add_book_form,
        "books": books
    })


В данном коде мы проверяем, была ли форма отправлена. Если она была отправлена, мы получаем данные из формы, которую заполнил пользователь. Далее мы проверяем, валидна ли заполненная пользователем форма, используя функцию is_valid(). Она возвращает True, если в форме нет ошибок.

Как только в данных формы нет ошибок, мы сохраняем новую запись в базе данных, используя метод save(). Кроме того, поскольку мы представляем пользователям все книги, содержащиеся в каталоге, мы получаем все книги. Мы передаем их внутрь контекстного словаря, чтобы получить доступ к данным из нашего файла шаблона.

Осталось обновить наш шаблон, для этого в файл home.html добавим следующий код:

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">

    <title>E-library Application</title>
  </head>
  <body>
      <div class="container">
        <h1>E-library Application &#128218;</h1>
        <hr>
        <div class="row">

          <div class="col-md-6">          
        {% for book in books %}
          <div class="card mb-4">
            <div class="card-header">
                {{ book.ISBN }}
            </div>
            <div class="card-body">
                <h5 class="card-title">{{ book.title }}</h5>
                <p class="card-text">
                    <em>By:</em> {{ book.author }}
                    <br>
                    <br>
                    <strong>Availability:</strong> {{ book.availability }}
                  </p>
              </div>
          </div>      
        
        {% empty %}
          <p>Empty...</p>
        {% endfor %}
        </div>               
            <div class="col-md-6">            

                <form method="post">
                    {% csrf_token %}
                   
                    {{ add_book_form.as_p }}
                    
                      <br>
                      <div class="text-center">
                        <input type="submit" class="btn btn-warning" style="width: 100%" value="Add Book">
                      </div>
                   </form>
            </div>
            </div>
        </div>
  </body>
</html>

Ранее мы жестко запрограммировали некоторые демонстрационные данные. Теперь мы отображаем данные в шаблоне, присутствующие в базе данных. Вспомните, что в файле home.html мы добавили переменную books, содержащую данные из каталога.

Запустим тесты

Выполним следующую команду:

python manage.py test

И мы получаем несколько неудачных тестов. Это показывает, что добавленный нами код вызывает ошибку.

Когда мы проходим неудачный тест, мы видим, что мы получаем исключение AssertionError, показанное ниже:

AssertionError: Database queries to 'default' are not allowed in SimpleTestCase subclasses. Either subclass TestCase or TransactionTestCase to ensure proper test isolation or add 'default' to elibrary_app.tests.CatalogueFormTests.databases to silence this failure.

 

Как пройти все тесты?

Кажется, что модификация, которую мы сделали в файле views.py, теперь связана с подключением к базе данных. Большинство тестов, которые мы написали на предыдущих уроках, использовали класс SimpleTestCase.

До этого момента, несмотря на то, что мы взаимодействовали с нашим представлением, тест все еще выполнялся успешно. Однако, поскольку мы ввели соединение с базой данных в представлении, нам нужно обновить все тестовые классы, чтобы использовать класс TestCase.

Вот тестовые классы, которые нам нужно обновить:

  • CatalogTemplateTests
  • CatalogFormTests
  • ElibraryURLsTest

После обновления трех тестовых классов для использования TestCase класса мы повторно запускаем тесты, и они должны успешно пройти.

В итоге мы получаем следующий файл с нашими тестами:

from django.test import TestCase
from .models import Catalog
from django.urls import reverse
from django.urls.base import resolve
from .views import home
from .forms import AddBookForm


class CatalogueViewTests(TestCase):
    """    Тест для представлений    """
    def test_book_list_view(self):

        Book_1 = Catalog.objects.create(
            title='Django for Beginners (2018)',
            ISBN='978-1-60309-0',
            author='John Doe',
            price=9.99,
            availability='true'
        )

        Book_2 = Catalog.objects.create(
            title='Django for Professionals (2020)',
            ISBN='978-1-60309-3',
            author='Mary Doe',
            price=11.99,
            availability='false'
        )

        response = self.client.get(reverse('home'))

        self.assertIn('Django for Professionals (2020)', response.content.decode())
        self.assertIn('John Doe', response.content.decode())
        self.assertIn('978-1-60309-3', response.content.decode())

class CatalogTemplateTests(TestCase):
    """    Тест шаблона    """
    def test_homepage_template(self):
        response = self.client.get(reverse('home'))
        self.assertTemplateUsed(response, 'home.html')

    def test_homepage_contains_correct_html(self):
        response = self.client.get(reverse('home'))
        self.assertContains(response, 'E-library Application')

    def test_homepage_does_not_contain_incorrect_html(self):
        response = self.client.get(reverse('home'))
        self.assertNotContains(response, 'Hello World')


class CatalogFormTests(TestCase):
    """    Тесты для форм    """

    def setUp(self):
        url = reverse('home')
        self.response = self.client.get(url)

    def test_book_form(self):
        form = self.response.context.get('add_book_form')
        self.assertIsInstance(form, AddBookForm)
        self.assertContains(self.response, 'csrfmiddlewaretoken')

    def test_bootstrap_class_used_for_default_styling(self):
        form = self.response.context.get('add_book_form')
        self.assertIn('class="form-control"', form.as_p())

    def test_book_form_validation_for_blank_items(self):
        add_book_form = AddBookForm(
            data={'title': '', 'ISBN': '', 'author': '', 'price': '', 'availability': ''}
        )
        self.assertFalse(add_book_form.is_valid())


class ElibraryURLsTest(TestCase):
    """    Тестируем URLs    """

    def test_homepage_url_name(self):
        response = self.client.get(reverse('home'))
        self.assertEqual(response.status_code, 200)

    def test_root_url_resloves_to_homepage_view(self):
        found = resolve('/')
        self.assertEquals(found.func, home)


class CatalogModelTests(TestCase):
    """    Тест модели каталога    """

    def setUp(self):
        self.book = Catalog(
            title='First Django Book',
            ISBN='978-1-60309-3',
            author='Ilya Perminov',
            price='9.99',
            availability='True'
        )

    def test_create_book(self):
        self.assertIsInstance(self.book, Catalog)

    def test_str_representation(self):
        self.assertEquals(str(self.book), "First Django Book")

    def test_saving_and_retrieving_book(self):
        first_book = Catalog()
        first_book.title = 'First Django Book'
        first_book.ISBN = '978-1-60309-3'
        first_book.author = 'Ilya Perminov'
        first_book.price = '9.99'
        first_book.availability = 'True'
        first_book.save()

        second_book = Catalog()
        second_book.title = 'Second Django Book'
        second_book.ISBN = '978-3-60124-1'
        second_book.author = 'Dmitry Seleznev'
        second_book.price = '19.99'
        second_book.availability = 'False'
        second_book.save()

        saved_books = Catalog.objects.all()
        self.assertEqual(saved_books.count(), 2)

        first_saved_book = saved_books[0]
        second_saved_book = saved_books[1]
        self.assertEqual(first_saved_book.title, 'First Django Book')
        self.assertEqual(second_saved_book.author, 'Dmitry Seleznev')


Теперь мы можем проверить, все тесты проходят.


Будьте вежливы и соблюдайте наши принципы сообщества. Пожалуйста, не оставляйте решения и подсказки в комментариях, для этого есть отдельный форум.
Оставить комментарий

В обновленных тестах, как и в первом тесте сохранения книг, мы передаем 17 значное значение ISBN. В предыдущих уроках ошибка исправлена, а в этом осталась. Нужно сократить до "978-3-16-1484"

@Михаил_Зайнутдинов, Спасибо, поправили.