Django Rest Framework не может исправить Serializer (многие = True). Данные в тесте представления - PullRequest
0 голосов
/ 13 сентября 2018

Я выполняю модульное тестирование представления и пытаюсь пропатчить свойство .data на моем сериализаторе, но похоже, что оно ведет себя по-разному, когда many=True kwarg передается конструктору сериализатора и, следовательно, не корректно исправляет. Вот обобщенный пример моего кода.

# myapp/serializers.py
class MySerializer(serializers.Serializer):
    some_field = serializers.CharField()


# myapp/views.py
class MyView(View):

    def get(self, request):
        # ..stuff
        some_data = []
        serializer = MySerializer(some_data, many=True)
        print(type(serializer))  # <class 'rest_framework.serializers.ListSerializer'>
        print(type(serializer.data))  # <class 'rest_framework.utils.serializer_helpers.ReturnList'>


        return Response({"data": seralizer.data, status=200})


# in tests
def test_view_case_one(mocker):

    # setup other mocks

    serialized_data = mocker.patch("myapp.views.MySerializer.data", new_callable=mocker.PropertyMock)

    # invoke view
    response = MyView().get(fake_request)

    # run assertions
    serialized_data.assert_called_once() # this says it's never called 

1 Ответ

0 голосов
/ 13 сентября 2018

Раньше я сталкивался с проблемами, пытаясь исправить rest_framework.serializers.ListSerializer.data.Должно быть опечатка.Попытка повторной попытки и была успешно исправлена.Учитывая случай, many=True воссоздает сериализатор как ListSerializer Мне просто нужно было пропатчить свойство базового класса.

serialized_data = mocker.patch(
    "rest_framework.serializers.ListSerializer.data",
    new_callable=mocker.PropertyMock
)

Редактировать: более подробный ответ

Когда используется many=True, метод __new__ на BaseSerializer захватывает ваш класс и создает из него ListSerializer, и поэтому мой объект отображается как ListSerializer.Поскольку мы фактически получаем ListSerializer вместо нашего определенного класса, патч не применяется к методу ListSerializer.data.Соответствующие части исходного кода для BaseSerializer ниже

class BaseSerializer(Field):

    def __new__(cls, *args, **kwargs):
        # We override this method in order to automagically create
        # `ListSerializer` classes instead when `many=True` is set.
        if kwargs.pop('many', False):
            return cls.many_init(*args, **kwargs)
        return super(BaseSerializer, cls).__new__(cls, *args, **kwargs)

    @classmethod
    def many_init(cls, *args, **kwargs):
        """
        This method implements the creation of a `ListSerializer` parent
        class when `many=True` is used. You can customize it if you need to
        control which keyword arguments are passed to the parent, and
        which are passed to the child.

        Note that we're over-cautious in passing most arguments to both parent
        and child classes in order to try to cover the general case. If you're
        overriding this method you'll probably want something much simpler, eg:

        @classmethod
        def many_init(cls, *args, **kwargs):
            kwargs['child'] = cls()
            return CustomListSerializer(*args, **kwargs)
        """
        allow_empty = kwargs.pop('allow_empty', None)
        child_serializer = cls(*args, **kwargs)
        list_kwargs = {
            'child': child_serializer,
        }
        if allow_empty is not None:
            list_kwargs['allow_empty'] = allow_empty
        list_kwargs.update({
            key: value for key, value in kwargs.items()
            if key in LIST_SERIALIZER_KWARGS
        })
        meta = getattr(cls, 'Meta', None)
        list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
        return list_serializer_class(*args, **list_kwargs) 
...