Python Django Rest框架JWT单元测试

Python Django Rest框架JWT单元测试,python,django,django-rest-framework,jwt,Python,Django,Django Rest Framework,Jwt,我将DRF与JWT包一起用于身份验证。现在,我正在尝试编写一个单元测试,用JWT令牌对自己进行身份验证。无论我如何尝试,我都无法让测试API客户机通过JWT进行身份验证。如果我对API客户机(在我的例子中是Postman)也这样做,那么一切都会正常工作 这是测试用例: from django.urls import reverse from rest_framework.test import APITestCase from rest_framework_jwt.settings import

我将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
但我总是得到一个
401身份验证凭据,但没有提供

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

如果我重写测试以使用
rest\u framework.test.RequestsClient
,并实际将其发送到URL,它就会工作

有什么帮助吗


注意:我知道
force_authenticate()
和,但我希望我的单元测试能够像API客户端在生产中一样访问API。

尝试为此测试设置一个新的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)

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


受@dkarchmer启发,这是我的代码。
我使用的是自定义用户模型,电子邮件用于身份验证。
注意使用
电子邮件
字段进行身份验证请求。 如果我使用
用户名
,则响应为
400\u BAD\u请求
401\u UNAUTHORIZED
通常表示凭据不正确或用户未激活

def测试_异常(自身):
User=get\u User\u model()
电子邮件user@test.com'
密码='userpass1'
用户名='user'
user=user.objects.create\u user(
用户名=用户名,电子邮件=电子邮件,密码=密码)
user.is_active=False
user.save()
获取url=reverse('token\u获取\u对')
resp=self.client.post(
获取url,{'email':电子邮件,'password':password},format='json')
self.assertEqual(分别为status\u代码、status.HTTP\u 401\u未经授权)
user.is_active=True
user.save()
resp=self.client.post(
获取url,{'email':电子邮件,'password':password},format='json')
self.assertEqual(分别为status\u代码、status.HTTP\u 200\u OK)

邮递员与您的实际数据库交互。Django使用单独的数据库来运行测试用例。因此,在身份验证测试之前,需要在测试定义中再次创建新的用户记录。希望这能有所帮助。

如果您正在使用and和Python 3.6+,则以下答案适用。您需要创建一个fixture,我称之为
api\u client
,您需要为现有用户获取令牌

来自django.contrib.auth.models导入用户
从rest\u framework.test导入APIClient
从rest\u framework\u simplejwt.tokens导入RefreshToken
导入pytest
@pytest.fixture
def api_客户端():
user=user.objects.create_user(用户名='john',电子邮件='wwwjs@js.com“,password='js.sj')
client=APIClient()
refresh=RefreshToken.for_用户(用户)
client.credentials(HTTP_AUTHORIZATION=f'Bearer{refresh.access_token})
返回客户端
注意,在上面的fixture中,用户是在那里创建的,但是您可以使用另一个fixture来创建用户并将其传递给这个fixture。关键要素如下所示:

refresh=refreshtToken.for_用户(用户)
这一行允许您按照说明手动创建令牌。拥有该令牌后,您可以使用方法
credentials
来设置头,然后测试客户端将在所有后续请求中包含这些头。请注意,
refresh.access\u令牌
包含访问令牌

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

@pytest.mark.django\u db
def测试您的测试的名称(api客户端):
#在这里添加您的逻辑
url=reverse('your-url')
response=api\u client.get(url)
data=response.data
assert response.status_code==HTTP_200_OK
#你的断言

因为我使用的是django单元测试客户端,所以我刚刚创建了一个简单的基类,其中包含一个承载令牌属性:

import json

from django.test import TestCase
from django.contrib.auth import User

from rest_framework.test import APIClient
from rest_framework_simplejwt.tokens import RefreshToken


class TestCaseBase(TestCase):
    @property
    def bearer_token(self):
        # assuming there is a user in User model
        user = User.objects.get(id=1)
        client = APIClient()
        refresh = RefreshToken.for_user(user)
        return {"HTTP_AUTHORIZATION":f'Bearer {refresh.access_token}'}
在我的django单元测试中:

class SomeTestClass(TestCaseBase):
    url = "someurl"

    def test_get_something(self):
        self.client.get(self.url, **bearer_token)

    def test_post_something(self):
        self.client.post(self.url, data={"key":"value"}, **self.bearer_token)


谢谢不需要新的APIClient,因为
APITestCase
已经有一个。但是
HTTP\u授权
成功了!非常感谢。我使用的django rest框架simplejwt也是如此。使用
client.credentials(HTTP_AUTHORIZATION='Bearer'…
(而不是
AUTHORIZATION='Bearer'…
)后,一切正常!我在
resp=self.client.post(url,{'username':'user@foo.com','password':'pass'},format='json')
使用我正确的用户名/密码。我收到了200封邮递员的回复,但使用此代码得到了401封。当我每天运行单元测试时,代码仍然有效。你应该用代码发布一个独立的问题,让别人帮你。我没能让它工作,因为我在中使用了user\u model.objects.create而不是user\u model.objects.create\u user这种情况发生在某人身上。主要区别在于创建用户在保存之前对密码进行哈希运算。你们两个让我开心。感谢Shope这可以帮助@alexei Marinichenko用户是在
test\u get\u member()的第一行创建的
谢谢@Christof。请确保密码已加密,以防万一。根据记忆,我遇到了这两个问题。祝你好运。为我完成了工作,正是我所需要的。谢谢!
class SomeTestClass(TestCaseBase):
    url = "someurl"

    def test_get_something(self):
        self.client.get(self.url, **bearer_token)

    def test_post_something(self):
        self.client.post(self.url, data={"key":"value"}, **self.bearer_token)