web-dev-qa-db-ja.com

Django Rest Framework JWTユニットテスト

認証にJWTパッケージでDRFを使用しています。ここで、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 Authentication credentials were not provided_を取得します。

_response.request_にはトークンが表示されていますが、適用されていないようです。

_rest_framework.test.RequestsClient_を使用するようにテストを書き直し、実際にそれを live_server URLに送信すると、動作します。

これについて何か助けはありますか?

追伸force_authenticate()login は知っていますが、本番環境のAPIクライアントと同じように、単体テストでAPIにアクセスしたいと思います。

11
Christof

このテスト用に新しいAPIClientをセットアップしてみてください。これは私自身のテストがどのように見えるかです

 def test_api_jwt(self):

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

    resp = self.client.post(url, {'email':'[email protected]', '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':'[email protected]', '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)
20
dkarchmer

私は同様の問題を抱えていましたが、比較するより多くのコード(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='[email protected]', 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)
2
rude4000