Запуск первых тестов

В мире Python есть несколько популярных фреймворков тестирования. В стандартную библиотеку языка входит фреймворк unittest, он принят в Django как основа для написания тестов. Помимо него есть и другие библиотеки, но мы будем тестировать код стандартными инструментами.
При создании нового приложения в его директории появился файл tests.py. Он пуст, но уже включен в инфраструктуру тестирования проекта.
Давайте запустим все тесты проекта (спойлер: их нет).
Скопировать кодBASH
(venv) $ python manage.py test System check identified no issues (0 silenced). ---------------------------------------------------------------------- Ran 0 tests in 0.000s OK # нет тестов — нет и ошибок!
Тестирование в Django организовано достаточно удобно и гибко. При выполнении команды общего запуска тестов система ищет файл tests.py в каждом приложении и запускает тесты в этом файле. Если в проекте много приложений, тесты запускаются по очереди.
Пора написать первый тест. Добавьте в файл posts/tests.py такой код:
Скопировать кодPYTHON
from django.test import TestCase # Create your tests here. class TestStringMethods(TestCase): def test_length(self): self.assertEqual(len('yatube'), 6)
В командной строке запустите выполнение тестов:
Скопировать кодBASH
(venv) $ python manage.py test Creating test database for alias 'default'... System check identified no issues (0 silenced). . # внимание, эта точка в выводе означает, что запущенный тест пройден ---------------------------------------------------------------------- Ran 1 test in 0.001s OK Destroying test database for alias 'default'...
Система обнаружила и запустила тест, он выполнился успешно.
Точка в выводе означает, что тест пройден успешно. При провале теста будет выведен символ F (Failed).
В больших проектах могут быть сотни или даже тысячи тестов, их совместный запуск отнимает много времени — до нескольких десятков минут. Если вы работаете над конкретной задачей, то не касающиеся её тесты отнимут время и не принесут пользы. Так что есть смысл запускать только часть тестов, указывая их адреса:
Скопировать кодBASH
# Запускаем все тесты приложения posts (venv) $ python manage.py test posts ...skip... # Запускаем все тесты приложения posts которые расположены в файле tests (venv) $ python manage.py test posts.tests ...skip... # Запускаем конкретный класс unit-тестов (venv) $ python manage.py test posts.tests.TestStringMethods ...skip... # Запускаем метод test_length() из класса TestStringMethods # из файла tests.py из директории posts (venv) $ python manage.py test posts.tests.TestStringMethods.test_length ...skip...
Дополнительные параметры запуска можно узнать, выполнив команду python manage.py test -h.

Терминология тестирования

Файл tests.py может содержать множество классов с методами-тестами. Такой файл называется «набор тестов» (на английском — test suite).
Каждый отдельный метод обычно тестирует работу какого-то одного логического элемента, фрагмента кода. По-английски такой кусочек кода называется test case, а на русском обычно хватает слова «тест».
Для тестирования бывают необходимы начальные данные, например — записи в базе или какой-то файл с исходными данными. Такие данные по-английски называются test fixtures; возможно, кто-нибудь и перевёл это выражение удачно, но в повседневной жизни это называют фикстурами.
Часть фреймворка тестирования, отвечающая за подготовку окружения и запуск тестов, по-английски называется test runner, но в русском языке эту часть обычно отдельно не выделяют.

Базовые предположения

Код тестов выглядит самобытно и непривычно, он не похож на знакомый вам код: вместо обычной логики тесты состоят из предположений (assertion), которые в ходе теста подтверждаются (в этом случае тест пройден) или опровергаются (тест провален).
Вернемся к тесту, который мы запускали:
Скопировать кодPYTHON
# Каждый логический набор тестов — это класс, # который наследуется от базового класса TestCase from django.test import TestCase # Каждый класс — это набор тестов. Имя такого класса принято начинать со слова Test. # В файле может быть множество наборов тестов, # не обязательно иметь один класс для всего приложения. class TestStringMethods(TestCase): # Каждый отдельный метод в наборе тестов должен начинаться со слова test # таких методов-тестов в наборе может быть множество. def test_length(self): # В этой строке находится собственно тест который проверяет # предположение (assertion) являются ли переданные параметры # эквивалентными (equal) self.assertEqual(len('yatube'), 6)
Выражение self.assertEqual(len('yatube'), 6) — ключевая строка теста, она проверяет предположение, что значение первого параметра эквивалентно второму параметру. Класс TestStringMethods унаследован от класса TestCase — это базовый класс для тестов в Django, он расширяет работу стандартного класса unittest.TestCase, добавляя к нему дополнительный набор предположений.
Вот доступный список простых методов-предположений:
Каждому из этих методов можно передать параметр msg, он делает вывод более информативным. Измените набор тестов в файле tests.py так:
Скопировать кодPYTHON
from django.test import TestCase class TestStringMethods(TestCase): def test_length(self): self.assertEqual(len('yatube'), 6) def test_show_msg(self): # действительно ли первый аргумент — True? self.assertTrue(False, msg="Важная проверка на истинность")
И выполните его:
Скопировать кодBASH
(venv) $ python manage.py test posts Creating test database for alias 'default'... System check identified no issues (0 silenced). .F # первый тест пройден (точка), второй тест провален (F) ====================================================================== FAIL: test_show_msg (posts.tests.TestStringMethods) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Dev/Yatube/yatube/posts/tests.py", line 11, in test_show_msg self.assertTrue(False, msg="Важная проверка на истинность") AssertionError: False is not true : Важная проверка на истинность # Нет, False — это не True! ---------------------------------------------------------------------- Ran 2 tests in 0.001s FAILED (failures=1) Destroying test database for alias 'default'...
Предположение оказалось ложным, тест провален, а мы получили явный и читаемый сигнал о том, что ожидалось в этом тесте.

Расширенный набор предположений

Django расширяет список базовых assert-методов, добавляя специфические для web-разработки методы тестирования форм, ответов view-функций и классов.
В работе нам пригодятся такие методы:
Расширенные методы работают достаточно интеллектуально. Вот пример из документации метода assertHTMLEqual: оба предложенных варианта сравнения не вызовут ошибки, тест будет пройден, хотя, на первый взгляд, сравниваемые фрагменты кода заметно отличаются.
Скопировать кодPYTHON
self.assertHTMLEqual( '<p>Hello <b>&#x27;world&#x27;!</p>', '''<p> Hello <b>&#39;world&#39;! </b> </p>''' ) self.assertHTMLEqual( '<input type="checkbox" checked="checked" id="id_accept_terms" />', '<input id="id_accept_terms" type="checkbox" checked>' )