При создании приложения Django автоматически создается tests.py.
Большинство разработчиков время от времени задаются таким вопросом.
“Этот файл... разве его не можно удалить?”
“Как его вообще использовать?”
На самом деле tests.py — это официальная точка входа для тестирования, рекомендуемая Django, и,
как только вы немного привыкнете, он станет действительно большой страховкой при рефакторинге, добавлении функций и обновлении версий. (Проект Django)
В этой статье:
-
Что такое файл
tests.py -
Как его писать и выполнять
-
В каких ситуациях он особенно полезен
-
Примеры реального использования
все это будет рассмотрено в одном месте.
1. Что такое tests.py?
Django использует тестовую библиотеку на основе unittest из стандартной библиотеки Python.
-
Создайте тестовые классы, унаследованные от
django.test.TestCase, -
внутри которых напишите методы, начинающиеся с
test_, -
и они будут автоматически найдены и выполнены с помощью команды
./manage.py test.
Когда вы создаете приложение с помощью startapp, по умолчанию создается файл tests.py:
-
Для небольшого проекта — достаточно одного
tests.py -
При росте проекта — рекомендуется разбить на пакет
tests/(test_models.py,test_views.pyи т.д.) в документации Django.
Таким образом, вы можете понимать это как основное местоположение для тестов на уровне приложения.
2. Когда это полезно? (Почему стоит использовать тесты)
Тестирование в Django особенно полезно в следующих ситуациях.
-
Проверка моделей/бизнес-логики
-
Чем сложнее логика расчетов, изменения состояния, или пользовательские методы,
-
тем более уверенно можно рефакторить после создания теста.
-
-
Проблемы с представлениями/URL/проверкой прав
-
Изменяется ли код ответа в зависимости от статуса аутентификации
-
Корректно ли обрабатывается некорректный ввод с перенаправлением / ошибкой
-
Передается ли правильный контекст в шаблон
-
-
Предотвращение регрессии
- Если вы поймали баг, задокументируйте его тестом, и он не повторится
-
Обеспечение стабильности при обновлении версий
-
Когда вы поднимаете версию Django или библиотек
-
вы можете запустить
./manage.py testи убедиться, что все работает
-
3. Самый простой пример tests.py
3-1. Тестирование моделей
# myapp/tests.py
from django.test import TestCase
from .models import Article
class ArticleModelTests(TestCase):
def setUp(self):
self.article = Article.objects.create(
title="Тестовый заголовок",
content="Содержимое",
views=0,
)
def test_increase_views(self):
# given
self.assertEqual(self.article.views, 0)
# when
self.article.increase_views()
# then
self.assertEqual(self.article.views, 1)
-
TestCase— это класс, расширяющийunittest.TestCaseдля Django. -
Каждый тест изолирован на уровне транзакции, и база данных откатывается после каждого теста, чтобы не было взаимовлияния.
4. Как тестировать представления/URL — используя Client
Django предоставляет тестовый HTTP-клиент django.test.Client.
Вы можете отправлять GET/POST запросы к URL и проверять ответы, не поднимая настоящий сервер.
4-1. Простой тест представления
# myapp/tests.py
from django.test import TestCase
from django.urls import reverse
from .models import Article
class ArticleViewTests(TestCase):
def setUp(self):
self.article = Article.objects.create(
title="Статья 1",
content="Содержимое",
views=0,
)
def test_article_detail_page_returns_200(self):
url = reverse("article_detail", args=[self.article.id])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Статья 1")
Основные моменты:
-
self.client: Экземпляр тестового клиента, предоставляемый классомTestCase -
reverse("article_detail", args=[...]): Получение URL по имени вместо хардкодинга -
assertContains: Проверка наличия определенной строки в теле ответа
4-2. Тестирование проверки прав и авторизации
from django.contrib.auth import get_user_model
class ArticlePermissionTests(TestCase):
def setUp(self):
self.user = get_user_model().objects.create_user(
username="tester",
password="pass1234",
)
self.article = Article.objects.create(
title="секрет",
content="Секретный текст",
views=0,
)
def test_anonymous_user_redirected_to_login(self):
url = reverse("article_edit", args=[self.article.id])
response = self.client.get(url)
self.assertEqual(response.status_code, 302)
self.assertIn("/accounts/login", response["Location"])
def test_logged_in_user_can_access_edit_page(self):
self.client.login(username="tester", password="pass1234")
url = reverse("article_edit", args=[self.article.id])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
-
необходимо проверить, происходит ли перенаправление при попытке доступа анонимного пользователя
-
нужно удостовериться, что авторизованный пользователь должен иметь доступ к странице редактирования
Это позволит на ранней стадии выявить ошибки связанные с правами.
5. Когда и как запускать tests.py?
5-1. Запуск всех тестов
$ python manage.py test
- Он автоматически находит и выполняет
файлыtest*.pyв текущем каталоге, содержащие под классыunittest.TestCaseилиdjango.test.TestCase.
5-2. Запуск только конкретного приложения
$ python manage.py test myapp
5-3. Запуск только конкретного модуля/класса/метода
# весь модуль myapp.tests
$ python manage.py test myapp.tests
# конкретный TestCase
$ python manage.py test myapp.tests.ArticleViewTests
# конкретный тест-метод
$ python manage.py test myapp.tests.ArticleViewTests.test_article_detail_page_returns_200
В процессе разработки полезно вырабатывать привычку быстро запускать только измененные части.
6. Как расширить tests.py в пакет тестов
Когда проект растет, одного tests.py становится недостаточно.
В документации Django также рекомендуется разбивать его на пакетную структуру.(Проект Django)
Например:
myapp/
tests/
__init__.py
test_models.py
test_views.py
test_forms.py
test_api.py
В каждом файле:
# myapp/tests/test_models.py
from django.test import TestCase
from myapp.models import Article
class ArticleModelTests(TestCase):
...
Разделение не влияет на выполнение.
$ python manage.py test myapp # все
$ python manage.py test myapp.tests.test_models # только тесты модели
7. Несколько реальных примеров использования
7-1. “Тест на воспроизведение ошибки” для предотвращения регрессии
-
Ошибка в продакшене
-
Напишите тест для воспроизведения в локальной среде (он должен упасть)
-
Исправьте код
-
Убедитесь, что тест проходит успешно
-
Сделайте
gitкоммит теста и исправленного кода вместе
Таким образом, в будущем, при рефакторинге, не возникнут те же самые ошибки.
7-2. Фиксация формата ответа API
Сотрудничая с фронтендом, формат ответа API становится очень важным.
class ArticleApiTests(TestCase):
def setUp(self):
self.article = Article.objects.create(
title="API заголовок",
content="Содержимое",
views=0,
)
def test_article_detail_api_response(self):
url = reverse("api:article-detail", args=[self.article.id])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertEqual(data["id"], self.article.id)
self.assertIn("title", data)
self.assertIn("content", data)
-
Явно укажите, какие поля обязательно должны быть включены в ответ
-
Если случайно изменить или удалить название поля, тест сразу же упадет
7-3. Тестирование логики валидации форм
from .forms import ArticleForm
class ArticleFormTests(TestCase):
def test_title_is_required(self):
form = ArticleForm(data={"title": "", "content": "Содержимое"})
self.assertFalse(form.is_valid())
self.assertIn("title", form.errors)
def test_valid_form(self):
form = ArticleForm(data={"title": "Заголовок", "content": "Содержимое"})
self.assertTrue(form.is_valid())
- Вы можете документировать правила валидации форм, оставляя их в виде тестов.

8. Резюме – не удаляйте tests.py, попробуйте использовать его правильно один раз
В одном предложении:
tests.py— это файл, создающий “страховку для приложения Django”.
-
Сначала это может быть утомительно,
-
но как только вы привыкнете к разработке на основе тестов,
-
стресс при рефакторинге/добавлении функций/обновлении версий значительно снизится.
В реальных проектах:
-
При создании новых функций – добавляйте как минимум 1–2 теста вместе с ними
-
При исправлении ошибок – всегда сначала напишите тест для воспроизведения этой ошибки
-
Когда начинаете масштабировать проект – выполните рефакторинг
tests.pyв пакетtests/
Если придерживаться этой модели, вы получите совершенно другой опыт разработки по сравнению с “проектом Django без тестов”.
Комментариев нет.