Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework JWT Unit Test

I am using DRF with the JWT package for authentication. Now, I'm trying to write a unit test that authenticates itself with a JWT token. No matter how I try it, I can't get the test API client to authenticate itself via JWT. If I do the same with an API client (in my case, Postman), everything works.

This is the test case:

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 

But I always get a 401 Authentication credentials were not provided.

In response.request I see the token is there, it's just not being applied I guess.

If I rewrite the test to use rest_framework.test.RequestsClient and actually send it to the live_server URL, it works.

Any help on this?

P.S.: I am aware of force_authenticate() and login, but I would like my unit tests to access the API the same as the API client will in production.

like image 607
Christof Avatar asked Nov 30 '17 15:11

Christof


People also ask

How do I check Django REST framework?

For Django REST Framework to work on top of Django, you need to add rest_framework in INSTALLED_APPS, in settings.py. Bingo..!! Django REST Framework is successfully installed, one case use it in any app of Django.

What is Django REST framework JWT?

JSON Web Token is an open standard for securely transferring data within parties using a JSON object. JWT is used for stateless authentication mechanisms for users and providers, this means maintaining session is on the client-side instead of storing sessions on the server.

What is APIRequestFactory?

APIRequestFactory : This is similar to Django's RequestFactory . It allows you to create requests with any http method, which you can then pass on to any view method and compare responses. APIClient : similar to Django's Client . You can GET or POST a URL, and test responses.


2 Answers

Try setting up a new APIClient for this test. This is how my own test looks like

 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) 
like image 66
dkarchmer Avatar answered Sep 20 '22 03:09

dkarchmer


The following answer applies if you are using Simple JWT and pytest, and Python 3.6+. You need to create a fixture, I have called it api_client, and you need to get the token for an existing user.

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='[email protected]', password='js.sj')     client = APIClient()     refresh = RefreshToken.for_user(user)     client.credentials(HTTP_AUTHORIZATION=f'Bearer {refresh.access_token}')      return client 

Notice that in the fixture above, the user is created there, but you can use another fixture to create the user and pass it to this one. The key element is the following line:

refresh = RefreshToken.for_user(user) 

This line allows you to create tokens manually as explained in the docs. Once you have that token, you can use the method credentials in order to set headers that will then be included on all subsequent requests by the test client. Notice that refresh.access_token contains the access token.

This fixture has to be used in your tests that you require the user to be authenticated as in the following example:

@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 
like image 26
lmiguelvargasf Avatar answered Sep 21 '22 03:09

lmiguelvargasf