在Django Rest Framework中进行单元测试时如何模拟对Serializer.is_valid()方法进行补丁

时间:2020-05-27 14:27:48

标签: python django django-rest-framework pytest

我希望能够在不连接到数据库的情况下对视图进行单元测试,我可以模拟修补User.save()方法,但不知道如何正确修补serializer.is_valid()。看来is_valid方法需要db连接来验证字段或类似的东西。有办法吗?

我正在使用django-pytest和pytest-mock

这是要测试的视图

class RegistrationViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
    permission_classes = (AllowAny,)
    serializer_class = RegistrationSerializer

    def create(self, request):
        user = request.data.get('user', {})
        serializer = self.serializer_class(data=user)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)

这是我的序列化器

class RegistrationSerializer(serializers.ModelSerializer):
    """Serializers registration request and creates a new user."""

    password = serializers.CharField(
        max_length=128,
        min_length=8,
        write_only=True   # can not be read by the client
    )
    token = serializers.CharField(max_length=255, read_only=True)

    class Meta:
        model = User
        fields = ['email', 'username', 'password', 'token']

    def create(self, validated_data):
        return User.objects.create_user(**validated_data)

到目前为止,这是我的视力测试

class TestRegistrationViewSet:
    """
    Test cases for registrating user
    """

    def test_register_user_success(self, rf, mocker):
        url = reverse('registration-list')
        data = {
            "user": {
                'username': 'user1',
                'email': "email@cookie.com",
                'password': 'user1pass'
            }
        }
        request = rf.post(url, data=data, content_type='application/json')

        mocker.patch.object(User, 'save')
        response = RegistrationViewSet.as_view({'post': 'create'})(request).render()
        assert response.status_code == status.HTTP_201_CREATED

这是我得到的错误回溯


repostery\authentication\views.py:21: in create
    serializer.is_valid(raise_exception=True)
.venv\lib\site-packages\rest_framework\serializers.py:234: in is_valid
    self._validated_data = self.run_validation(self.initial_data)
.venv\lib\site-packages\rest_framework\serializers.py:433: in run_validation
    value = self.to_internal_value(data)
.venv\lib\site-packages\rest_framework\serializers.py:490: in to_internal_value
    validated_value = field.run_validation(primitive_value)
.venv\lib\site-packages\rest_framework\fields.py:830: in run_validation
    return super().run_validation(data)
.venv\lib\site-packages\rest_framework\fields.py:566: in run_validation
    self.run_validators(value)
.venv\lib\site-packages\rest_framework\fields.py:588: in run_validators
    validator(value, self)
.venv\lib\site-packages\rest_framework\validators.py:73: in __call__
    if qs_exists(queryset):
.venv\lib\site-packages\rest_framework\validators.py:21: in qs_exists
    return queryset.exists()
.venv\lib\site-packages\django\db\models\query.py:777: in exists
    return self.query.has_results(using=self.db)
.venv\lib\site-packages\django\db\models\sql\query.py:538: in has_results
    return compiler.has_results()
.venv\lib\site-packages\django\db\models\sql\compiler.py:1121: in has_results
    return bool(self.execute_sql(SINGLE))
.venv\lib\site-packages\django\db\models\sql\compiler.py:1149: in execute_sql
    cursor = self.connection.cursor()
.venv\lib\site-packages\django\utils\asyncio.py:26: in inner
    return func(*args, **kwargs)
.venv\lib\site-packages\django\db\backends\base\base.py:260: in cursor
    return self._cursor()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <django.db.backends.postgresql.base.DatabaseWrapper object at 0x00000218938FA670>, name = None

    def _cursor(self, name=None):
>       self.ensure_connection()
E       RuntimeError: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.

.venv\lib\site-packages\django\db\backends\base\base.py:236: RuntimeError

2 个答案:

答案 0 :(得分:0)

您可以尝试将@ pytest.mark.django_db添加到测试中,或者可以像下面这样修补提到的方法: mocker.patch("project.views.RegistrationSerializer.is_valid")

答案 1 :(得分:0)

如果要测试的是is_valid()方法,则应修补该方法中打算访问数据库的部分,以免测试变得昂贵(因为访问数据库需要时间和资源)。例如,在这里我测试了当缺少字段时,我的序列化程序会引发正确的错误:

mocker.patch('rest_framework.validators.qs_exists', return_value=True)
        with pytest.raises(ValidationError):
            serializer.is_valid(raise_exception=True)