代码之家  ›  专栏  ›  技术社区  ›  Adam

Django Rest框架未能修补序列化程序(many=True)。视图中的数据测试

  •  1
  • Adam  · 技术社区  · 6 年前

    我正在对视图进行单元测试,并尝试修补 .data 属性,但当 many=True

    # 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 回复  |  直到 6 年前
        1
  •  1
  •   Adam    6 年前

    早些时候,我遇到了问题,试图修补 rest_framework.serializers.ListSerializer.data . 一定是打错了。重新尝试并成功修补。在这种情况下 many=True ListSerializer

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

    编辑:更深入的答案

    什么时候? 是用来 __new__ 上的方法 BaseSerializer 列表序列化程序 列表序列化程序 . 因为我们实际上收到了 列表序列化程序 而不是我们定义的类,补丁不适用于 ListSerializer.data 方法。源代码的相关部分 在下面

    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)