Ответ
Модульное (unit) тестирование API в Django фокусируется на проверке отдельных компонентов (например, сериализаторов, представлений, менеджеров) в изоляции от остальной системы.
Почему это важно? Это позволяет быстро выявлять ошибки в логике конкретных частей кода, упрощает отладку и обеспечивает высокую скорость выполнения тестов, так как не требуется полная загрузка Django-окружения или обращение к базе данных.
Основные подходы и инструменты:
unittest.TestCase
(илиpytest
): Для написания базовых тестовых классов и методов.- Мокирование (
unittest.mock
): Ключевой элемент unit-тестирования. Позволяет заменять внешние зависимости (БД, внешние API, кэш, другие компоненты Django) на контролируемые объекты, чтобы тестировать компонент изолированно. - Тестирование сериализаторов: Прямая проверка валидации и преобразования данных, без участия HTTP-запросов.
- Тестирование функций представлений (views): Вызов функций представлений напрямую, передача
HttpRequest
объектов (часто мокированных) и проверка возвращаемогоHttpResponse
.
Пример модульного теста для сериализатора:
from django.test import TestCase
from rest_framework import serializers
from datetime import date
# Пример простой модели (для демонстрации, в реальном проекте это была бы Django Model)
class MyModel:
def __init__(self, id, name, created_at):
self.id = id
self.name = name
self.created_at = created_at
# Пример сериализатора для MyModel
class MyModelSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(max_length=100, required=True)
created_at = serializers.DateField(read_only=True)
def create(self, validated_data):
# В реальном приложении здесь было бы создание объекта Django Model
return MyModel(id=1, **validated_data, created_at=date.today())
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
return instance
class MyModelSerializerTest(TestCase):
def test_serializer_validation_success(self):
"""Проверка успешной валидации данных сериализатором."""
data = {'name': 'Test Item'}
serializer = MyModelSerializer(data=data)
self.assertTrue(serializer.is_valid()) # Ожидаем успешную валидацию
self.assertEqual(serializer.validated_data['name'], 'Test Item')
def test_serializer_validation_failure_empty_name(self):
"""Проверка валидации при пустом имени."""
data = {'name': ''}
serializer = MyModelSerializer(data=data)
self.assertFalse(serializer.is_valid()) # Ожидаем ошибку валидации
self.assertIn('name', serializer.errors) # Проверяем наличие ошибки для поля 'name'
def test_serializer_create_method(self):
"""Проверка метода create сериализатора."""
data = {'name': 'New Item'}
serializer = MyModelSerializer(data=data)
serializer.is_valid(raise_exception=True)
instance = serializer.save() # Вызываем метод create
self.assertIsInstance(instance, MyModel)
self.assertEqual(instance.name, 'New Item')
self.assertEqual(instance.created_at, date.today())
Ключевые моменты модульного тестирования API:
- Фокус на изоляции: Тестируется один компонент за раз, минимизируя зависимости от других частей системы.
- Активное использование
unittest.mock
: Для имитации поведения базы данных, внешних API, кэша и других зависимостей, чтобы избежать реальных взаимодействий и обеспечить предсказуемость тестов. - Проверка внутренней логики: Тесты сосредоточены на корректности алгоритмов, валидации данных и обработке ошибок внутри конкретного компонента.
- Различие с интеграционными тестами: В отличие от интеграционных тестов,
APITestCase
изrest_framework.test
обычно не используется для чистого модульного тестирования, так как он имитирует полный HTTP-запрос и ответ, что выходит за рамки изоляции компонента.