Django Rest框架JWT单元测试

Chr*_*tof 10 python django jwt django-rest-framework

我正在使用DRF和JWT包进行身份验证.现在,我正在尝试编写一个使用JWT令牌验证自身的单元测试.无论我如何尝试,我都无法让测试API客户端通过JWT进行身份验证.如果我对API客户端(在我的情况下,Postman)也这样做,一切正常.

这是测试用例:

from django.urls import reverse
from rest_framework.test import APITestCase
from rest_framework_jwt.settings import api_settings

from backend.factories import member_factory

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER


class MemberTests(APITestCase):
    def test_get_member(self):
        member = member_factory()

        payload = jwt_payload_handler(member.user)
        token = jwt_encode_handler(payload)

        self.client.credentials(Authorization='JWT {0}'.format(token))
        response = self.client.get(reverse('member-detail', kwargs={'pk': member.pk}))
        assert response.status_code == 200
Run Code Online (Sandbox Code Playgroud)

但我总是得到一个401 Authentication credentials were not provided.

response.request我看到令牌在那里,它只是没有被应用我想.

如果我重写要使用的测试rest_framework.test.RequestsClient并将其实际发送到live_server URL,它就可以工作.

对此有何帮助?

PS我知道force_authenticate()登录但我想我的单元测试访问API与API客户端将在生产中相同.

dka*_*mer 21

尝试为此测试设置新的APIClient.这就是我自己的测试的样子

 def test_api_jwt(self):

    url = reverse('api-jwt-auth')
    u = user_model.objects.create_user(username='user', email='user@foo.com', password='pass')
    u.is_active = False
    u.save()

    resp = self.client.post(url, {'email':'user@foo.com', 'password':'pass'}, format='json')
    self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST)

    u.is_active = True
    u.save()

    resp = self.client.post(url, {'username':'user@foo.com', 'password':'pass'}, format='json')
    self.assertEqual(resp.status_code, status.HTTP_200_OK)
    self.assertTrue('token' in resp.data)
    token = resp.data['token']
    #print(token)

    verification_url = reverse('api-jwt-verify')
    resp = self.client.post(verification_url, {'token': token}, format='json')
    self.assertEqual(resp.status_code, status.HTTP_200_OK)

    resp = self.client.post(verification_url, {'token': 'abc'}, format='json')
    self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST)

    client = APIClient()
    client.credentials(HTTP_AUTHORIZATION='JWT ' + 'abc')
    resp = client.get('/api/v1/account/', data={'format': 'json'})
    self.assertEqual(resp.status_code, status.HTTP_401_UNAUTHORIZED)
    client.credentials(HTTP_AUTHORIZATION='JWT ' + token)
    resp = client.get('/api/v1/account/', data={'format': 'json'})
    self.assertEqual(resp.status_code, status.HTTP_200_OK)
Run Code Online (Sandbox Code Playgroud)

  • 谢谢!我使用的 django-rest-framework-simplejwt 也是如此。使用 `client.credentials(HTTP_AUTHORIZATION='Bearer <token>' ...` (而不是 `Authorization='Bearer <token>'...`,一切正常! (3认同)
  • 谢谢。不需要新的 APIClient,因为“APITestCase”已经有一个。但“HTTP_AUTHORIZATION”成功了! (2认同)
  • 我收到 401 错误 `resp = self.client.post(url, {'username':'user@foo.com', 'password':'pass'}, format='json')` 使用正确的用户名/密码。我通过 Postman 得到 200 响应,但通过此代码得到 401 响应。 (2认同)

lmi*_*asf 16

如果您使用的是Simple JWTpytest以及 Python 3.6+,则以下答案适用。您需要创建一个固定装置,我称之为api_client,并且您需要为现有用户获取令牌。

from django.contrib.auth.models import User
from rest_framework.test import APIClient
from rest_framework_simplejwt.tokens import RefreshToken

import pytest


@pytest.fixture
def api_client():
    user = User.objects.create_user(username='john', email='js@js.com', password='js.sj')
    client = APIClient()
    refresh = RefreshToken.for_user(user)
    client.credentials(HTTP_AUTHORIZATION=f'Bearer {refresh.access_token}')

    return client
Run Code Online (Sandbox Code Playgroud)

请注意,在上面的设备中,用户是在那里创建的,但是您可以使用另一个设备来创建用户并将其传递给这个设备。关键元素是以下行:

refresh = RefreshToken.for_user(user)
Run Code Online (Sandbox Code Playgroud)

此行允许您按照文档中的说明手动创建令牌。拥有该令牌后,您可以使用该方法credentials来设置标头,然后这些标头将包含在测试客户端的所有后续请求中。请注意,refresh.access_token其中包含访问令牌。

必须在您要求用户进行身份验证的测试中使用此装置,如下例所示:

@pytest.mark.django_db
def test_name_of_your_test(api_client):
    # Add your logic here
    url = reverse('your-url')
    response = api_client.get(url)
    data = response.data

    assert response.status_code == HTTP_200_OK
    # your asserts
Run Code Online (Sandbox Code Playgroud)

  • 为我完成了工作,正是我所需要的。谢谢你! (2认同)

小智 7

我有类似的问题,附上我向您发送我的解决方案只是为了有更多代码进行比较(tests.py)。

from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from django.contrib.auth.models import User


class AuthViewsTests(APITestCase):

    def setUp(self):
        self.username = 'usuario'
        self.password = 'contrasegna'
        self.data = {
            'username': self.username,
            'password': self.password
        }

    def test_current_user(self):

        # URL using path name
        url = reverse('tokenAuth')

        # Create a user is a workaround in order to authentication works
        user = User.objects.create_user(username='usuario', email='usuario@mail.com', password='contrasegna')
        self.assertEqual(user.is_active, 1, 'Active User')

        # First post to get token
        response = self.client.post(url, self.data, format='json')
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.content)
        token = response.data['token']

        # Next post/get's will require the token to connect
        self.client.credentials(HTTP_AUTHORIZATION='JWT {0}'.format(token))
        response = self.client.get(reverse('currentUser'), data={'format': 'json'})
        self.assertEqual(response.status_code, status.HTTP_200_OK, response.content)
Run Code Online (Sandbox Code Playgroud)