Сегодня мы займемся тестированием сериализаторов (serializers), чтобы убедиться, что они корректно обрабатывают входящие данные и возвращают предсказуемые результаты. Ну и попутно вы узнаете о таких полезных инструментах, как фикстуры — чтобы тесты стали понятнее и удобнее.
Сериализаторы в Django REST Framework играют ключевую роль. Они служат "переводчиками" между Python-объектами (моделями) и форматом JSON (или другим представлением данных). Если сериализатор работает неправильно, ваше API может рассыпаться как карточный домик. Например:
- Неправильная валидация данных может привести к сохранению некорректных записей в базу данных.
- Ошибки в методах
create()иupdate()могут повредить данные. - Некорректная настройка полей сериализатора приведет к тому, что пользователи API будут видеть и пользоваться неправильной структурой данных.
Тестирование сериализаторов поможет избежать подобных ситуаций. А главное — оно убережет вас от бессонных ночей перед дедлайном.
Задачи тестирования сериализаторов
Когда мы говорим о тестировании сериализаторов, основные задачи включают:
- Проверку, что сериализатор корректно "собирает" данные (пример: модель -> JSON).
- Проверку, что сериализатор валидирует данные так, как ожидается.
- Проверку правильной работы переопределенных методов, таких как
create()иupdate(). - Проверку работы вложенных сериализаторов, если такие используются.
- Проверку того, что сериализатор возвращает необходимые ошибки, если данные некорректны.
Устройство теста для сериализатора: общий подход
Прежде чем перейти к коду, давайте уясним общую структуру тестов:
- Тестовые данные (входные данные и ожидаемый результат).
- Создание экземпляра сериализатора.
- Проверка:
- Корректности сериализации (правильность выходных данных).
- Валидации входных данных (как валидных, так и невалидных примеров).
- Корректной работы методов
create()иupdate().
Создание тестируемого сериализатора
Для примера будем использовать приложение для управления задачами. Вот упрощённая модель задачи:
# models.py
from django.db import models
class Task(models.Model):
title = models.CharField(max_length=200)
description = models.TextField(blank=True)
is_completed = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
И соответствующий для неё сериализатор:
# serializers.py
from rest_framework import serializers
from .models import Task
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ['id', 'title', 'description', 'is_completed', 'created_at']
# Переопределяем метод create, чтобы добавить логику
def create(self, validated_data):
validated_data['title'] = validated_data['title'].capitalize()
return super().create(validated_data)
# Переопределяем метод update, чтобы изменить логику обновления
def update(self, instance, validated_data):
if 'is_completed' in validated_data and validated_data['is_completed']:
instance.description += "\nЗадача завершена."
return super().update(instance, validated_data)
Написание тестов для сериализатора
Теперь перейдём к тестам!
Тестирование сериализации
Мы проверяем, что сериализатор правильно преобразует модель в JSON. Например:
# tests/test_serializers.py
from rest_framework.test import APITestCase
from tasks.models import Task
from tasks.serializers import TaskSerializer
class TaskSerializerTestCase(APITestCase):
def test_task_serialization(self):
"""Проверка правильности сериализации Task -> JSON"""
task = Task.objects.create(
title="Test task",
description="Description for test",
is_completed=False
)
serializer = TaskSerializer(task)
expected_data = {
'id': task.id,
'title': "Test task",
'description': "Description for test",
'is_completed': False,
'created_at': task.created_at.isoformat() # Дата в формате ISO
}
self.assertEqual(serializer.data, expected_data)
Тестирование валидации
Теперь проверяем валидацию входящих данных:
def test_validation_success(self):
"""Проверка валидации корректных данных"""
valid_data = {
'title': "New Task",
'description': "Task description",
'is_completed': False
}
serializer = TaskSerializer(data=valid_data)
self.assertTrue(serializer.is_valid())
def test_validation_fail(self):
"""Проверка валидации некорректных данных"""
invalid_data = {
'title': "", # Пустой заголовок
'description': "Task description",
'is_completed': False
}
serializer = TaskSerializer(data=invalid_data)
self.assertFalse(serializer.is_valid())
self.assertIn('title', serializer.errors)
Тестирование методов create и update
Теперь убедимся, что методы create() и update() работают как ожидается:
def test_create_method(self):
"""Проверка метода create"""
data = {
'title': "new task",
'description': "new description",
'is_completed': False
}
serializer = TaskSerializer(data=data)
serializer.is_valid(raise_exception=True)
task = serializer.save()
self.assertEqual(task.title, "New task") # Заголовок должен быть с большой буквы
def test_update_method(self):
"""Проверка метода update"""
task = Task.objects.create(
title="Old task",
description="Some description",
is_completed=False
)
data = {'is_completed': True}
serializer = TaskSerializer(task, data=data, partial=True)
serializer.is_valid(raise_exception=True)
updated_task = serializer.save()
self.assertTrue(updated_task.is_completed)
self.assertIn("Задача завершена.", updated_task.description)
Использование фикстур для тестирования
Чтобы не повторять код создания объектов в каждом тесте, можно использовать фикстуры. Это просто заранее подготовленные данные.
# tests/conftest.py
import pytest
from tasks.models import Task
@pytest.fixture
def task():
return Task.objects.create(
title="Fixture task",
description="Fixture description",
is_completed=False
)
Теперь мы можем использовать их в тестах, указав имя фикстуры как аргумент функции:
def test_task_serialization_with_fixture(task):
serializer = TaskSerializer(task)
assert serializer.data['title'] == "Fixture task"
Полезные советы
- Тестируйте каждый аспект в отдельности. Если ваш сериализатор сложный, создайте разные тестовые методы для проверки отдельных частей функционала.
- Меньше повторений. Используйте фикстуры и вспомогательные функции для повторяющихся операций. Это сделает код тестов чище.
- Не забудьте про отрицательные тесты. Убедитесь, что сериализатор корректно отлавливает невалидные данные и возвращает ожидаемые ошибки.
Заключение
Теперь вы умеете тестировать сериализаторы! Эти навыки помогут вам убедиться, что API работает корректно на уровне данных. Это не только улучшит ваш код, но и сделает жизнь проще для всех пользователей вашего API, включая ваш фронтенд-команду (которая, кстати, вас за это полюбит!).
Переходите к тестированию представлений, и да начнутся интеграционные тесты!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ