Вопрос или проблема
У меня есть одна проблема при написании тестового кода для списка.
Для списка DRF ответ включает несколько данных.
Что мне делать, если я предполагаю, что объект response.data является моделью с множеством полей?
response.data --> [{"id": 1, "field1": "xxx", "field2": "yyy"....}, {"id": 2, ...}, ...]
Для сопоставления со статическими данными требуется слишком много входных данных, когда возвращается много полевых данных.
def test_xx(self):
self.assertEqual(response.data, [{"id": 1, "field1": "xxx", ..., "field12": "ddd", ...}, ...])
В настоящее время я пришел к следующему способу.
Я создал пример модели под названием Dog.
class Dog(models.Model):
...
hungry = models.BooleanField(default=False)
hurt = models.BooleanField(default=False)
Например, пусть будет класс Dog, и в представлении есть логика для получения списка голодных собак.
Я собираюсь сделать тест на логику получения списка голодных собак.
class ...
def test_dog_hungry_list(self):
response = client.get("/api/dog/hungry/")
expected_response = Dog.objects.filter(hungry=True)
self.assertEqual(response.data, expected_response)
Два значения, которые я хочу сравнить с assertEqual, response и expected_response, имеют разные типы.
- response –> Response
- expected_response –> Queryset
В случае response.data остается применить сериализатор, поэтому, когда он находится в формате dict, я хочу сравнить его с типом Queryset expected_response.
Я подумал о двух способах как следует.
- Применить сериализатор к Queryset для сравнения. –> (Serializer(expected_response, many=True))
- Сравнить значения первичных ключей.
пример номер 2.
expected_response = Dog.objects.filter(hungry=True).values("id")
self.assertEqual(
[data["id"] for data in response.data],
[data["id"] for data in expected_response]
)
Но я не уверен, являются ли два метода хорошими для сравнения.
Есть ли способ сделать данные разумными для сравнения, когда тестовый код возвращает два или три данных в форме большого dict?
Ответ или решение
Для написания тестов для представлений списка (list view) в Django REST Framework (DRF), особенно когда ваша модель имеет множество полей, действительно важно находить элегантный и эффективный способ проверки данных ответа. Приведу несколько примеров и рекомендаций по этому вопросу.
Подходы к тестированию
1. Сравнение путем сериализации QuerySet
Первый и наиболее распространенный способ — это сериализация ожидаемого QuerySet
, как вы уже упомянули. Это позволит вам привести данные из базы к тому же формату, который возвращает ваш API.
Пример кода:
from rest_framework.test import APITestCase
from myapp.models import Dog
from myapp.serializers import DogSerializer
class DogListTestCase(APITestCase):
def setUp(self):
# Создаем тестовые данные
Dog.objects.create(id=1, hungry=True, hurt=False)
Dog.objects.create(id=2, hungry=True, hurt=True)
Dog.objects.create(id=3, hungry=False, hurt=False)
def test_dog_hungry_list(self):
response = self.client.get("/api/dog/hungry/")
expected_response = Dog.objects.filter(hungry=True)
expected_data = DogSerializer(expected_response, many=True).data
self.assertEqual(response.data, expected_data)
В этом примере мы сериализуем expected_response
, чтобы проверить его с данными, возвращенными API.
2. Сравнение по ключевым полям
Если количество полей в ответе велико, и вас интересуют только некоторые ключевые атрибуты, вы можете ограничиться сравнением значений конкретных полей, таких как id
. Это позволяет сократить объем требуемого кода и все еще дает вам уверенность в корректности ответа.
Пример кода:
def test_dog_hungry_ids(self):
response = self.client.get("/api/dog/hungry/")
expected_ids = Dog.objects.filter(hungry=True).values_list("id", flat=True)
self.assertEqual(
sorted([dog["id"] for dog in response.data]),
sorted(expected_ids),
)
В этом случае сравниваются только id
, что значительно упрощает тест.
3. Использование визуализации данных
Если вы ожидаете, что ответ будет включать много полей, вы можете использовать assertListEqual
, чтобы сравнить списки объектов или словарей. Назовем это "глубоким сравнением", если вы хотите проверить определенные атрибуты.
def test_dog_hungry_list_deep_compare(self):
response = self.client.get("/api/dog/hungry/")
expected_data = Dog.objects.filter(hungry=True).values()
response_data = response.data
self.assertListEqual(sorted(response_data, key=lambda x: x['id']), sorted(expected_data, key=lambda x: x['id']))
Заключение
Выбор метода зависит от ваших целей и структуры данных. Если вам нужно проверить конкретные поля, используйте подход с сериализацией, а если достаточно проверки ключевых идентификаторов, второй вариант будет более эффективным. Важно стремиться к балансу между полнотой теста и его удобством.
При использовании DRF всегда лучше ориентироваться на читаемость и поддерживаемость кода тестов, чтобы другие разработчики могли быстро понять логику тестирования.