JavaRush /Курсы /Модуль 3: Django /Создание простых тестов для API

Создание простых тестов для API

Модуль 3: Django
22 уровень , 3 лекция
Открыта

Прежде чем окунуться в написание кодовых строк, давайте разберемся с основной идеей. Тесты — это способ убедиться, что код делает то, что должен делать. Они помогают нам находить ошибки, прежде чем их обнаружат пользователи, а это, поверьте, куда приятнее! Представьте, что ваш API — это самолет, а тесты — это его техническое обслуживание перед взлетом.

Чем тщательнее проверены двигатели, баки и шасси, тем меньше вероятность, что полет завершится катастрофой.

Зачем тестировать API?

API — это фасад вашего приложения. Это то, как ваши данные "разговаривают" с внешним миром. Если работа API нарушается, разработчики фронтенда, приложения или бэк-офиса сразу же начинают страдать. Поэтому тестирование API помогает:

  1. Проверить правильность HTTP-ответов.
  2. Убедиться, что данные корректно отправляются и принимаются.
  3. Проверить, что бизнес-логика работает так, как задумано.
  4. Защитить код от неожиданных изменений, которые могут сломать существующую функциональность.

Основы тестирования в Django

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

Для тестирования нашего API мы будем использовать два основных инструмента:

  1. Django TestCase для управления средой тестирования.
  2. DRF Test Client для взаимодействия с REST API.

Первый тест: наш "Hello, World!"

Начнем с чего-то простого. Предположим, у нас есть эндпоинт /api/ping/, который возвращает JSON-ответ с сообщением: {"message": "pong"}. Мы хотим убедиться, что этот эндпоинт работает правильно.

Добавим маршрутизацию для эндпоинта в urls.py нашего приложения myapp:

# myapp/urls.py
from django.urls import path
from django.http import JsonResponse

def ping_view(request):
    return JsonResponse({"message": "pong"})

urlpatterns = [
    path('api/ping/', ping_view, name='ping'),
]

Теперь создадим файл для тестов test_ping.py и напишем наш первый тест:

# myapp/tests/test_ping.py
from django.test import TestCase
from django.urls import reverse

class PingAPITestCase(TestCase):
    def test_ping_endpoint_returns_pong(self):
        """
        Тест для проверки эндпоинта /api/ping/
        """
        # Генерируем URL на основе имени маршрута
        url = reverse('ping')

        # Отправляем GET-запрос на эндпоинт
        response = self.client.get(url)

        # Проверяем статус ответа (должен быть 200 OK)
        self.assertEqual(response.status_code, 200)

        # Проверяем содержимое JSON-ответа
        self.assertEqual(response.json(), {"message": "pong"})

Что мы только что написали?

  1. Класс PingAPITestCase наследуется от TestCase, что дает нам доступ к тестовой среде.
  2. Метод test_ping_endpoint_returns_pong — это отдельный тест. Django обнаруживает все методы, названия которых начинаются с test_, как тестовые случаи.
  3. reverse('ping') генерирует URL для маршрута с именем ping, чтобы избежать хардкодинга.
  4. self.client.get(url) отправляет GET-запрос на указанный URL.
  5. assertEqual проверяет, совпадают ли два значения: статус ответа и содержимое ответа.

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

pytest

Если все настроено правильно, вы увидите зелененькую галочку — тест пройден! 🎉

Организация тестов: классы и файлы

Хотя один тест можно хранить где угодно, этого явно недостаточно для реального проекта. Следует придерживаться соглашений. Вот пример структуры:

myapp/
├── tests/
│   ├── __init__.py
│   ├── test_ping.py
│   ├── test_users.py
│   └── test_posts.py
└── ...

Каждый файл тестов фокусируется на одной области приложения:

  • test_ping.py — тесты для базовых функциональностей API.
  • test_users.py — тесты для пользовательских операций.
  • test_posts.py — тесты для работы с постами.

Тестирование статусов и ошибок

Разберем более сложный пример. Предположим, у нас есть эндпоинт /api/users/ для создания пользователя. Он принимает JSON-данные с именем пользователя и возвращает 400 Bad Request, если данные отсутствуют.

Вот упрощенное представление нашего представления:

# myapp/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

class UserCreateAPIView(APIView):
    def post(self, request):
        username = request.data.get('username')
        if not username:
            return Response(
                {"error": "Username is required"},
                status=status.HTTP_400_BAD_REQUEST
            )
        return Response({"message": f"User {username} created"}, status=status.HTTP_201_CREATED)

И добавляем его в маршруты:

# myapp/urls.py
from django.urls import path
from .views import UserCreateAPIView

urlpatterns = [
    path('api/users/', UserCreateAPIView.as_view(), name='user-create'),
]

Теперь тестируем:

# myapp/tests/test_users.py
from rest_framework.test import APITestCase
from rest_framework import status
from django.urls import reverse

class UserCreateAPITestCase(APITestCase):
    def test_create_user_success(self):
        """
        Тест успешного создания пользователя
        """
        url = reverse('user-create')
        data = {"username": "john_doe"}
        response = self.client.post(url, data, format='json')

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.json(), {"message": "User john_doe created"})

    def test_create_user_missing_username(self):
        """
        Тест ошибки при отсутствии имени пользователя
        """
        url = reverse('user-create')
        response = self.client.post(url, {}, format='json')

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(response.json(), {"error": "Username is required"})

Обратите внимание на следующие нюансы:

  • Мы используем APITestCase из DRF для тестирования API.
  • Ключевая настройка — format='json', чтобы явно указать формат отправляемых данных.

Полезные методы self.client

Для взаимодействия с API мы можем использовать следующие методы тест-клиента:

  • self.client.get(url, data, **kwargs)
  • self.client.post(url, data, **kwargs)
  • self.client.put(url, data, **kwargs)
  • self.client.delete(url, **kwargs)

Каждый из них помогает нам обращаться к соответствующим HTTP-методам для проверки API.

Типичные ошибки при написании тестов

  1. Забыли указать format='json'. Этот параметр обязателен, если API ожидает JSON-формат.
  2. Неправильно определили URL в reverse(). Убедитесь, что имя маршрута совпадает.
  3. Путаница в статусах. Проверьте ожидаемый статус (например, 400 vs 404).

Теперь вы знаете, как создавать базовые тесты для вашего API. Это важный шаг на пути к созданию стабильного и качественного кода. Впереди вас ждет еще множество возможностей для тестирования — а также больше зеленых галочек!

1
Задача
Модуль 3: Django, 22 уровень, 3 лекция
Недоступна
Тестирование GET-запроса
Тестирование GET-запроса
1
Задача
Модуль 3: Django, 22 уровень, 3 лекция
Недоступна
Проверка содержимого ответа
Проверка содержимого ответа
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ