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": "Ім'я користувача є обов'язковим"},
                status=status.HTTP_400_BAD_REQUEST
            )
        return Response({"message": f"Користувач {username} створений"}, 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": "Користувач john_doe створений"})

    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": "Ім'я користувача є обов'язковим"})

Зверніть увагу на такі нюанси:

  • Ми використовуємо 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. Це важливий крок на шляху до створення стабільного та якісного коду. Попереду вас чекає ще багато можливостей для тестування — а також більше зелених галочок!

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ