Reputation: 9908
The query to my endpoint works fine (as long as I pass it a valid token), it returns the json representation of my response data.
The code in the service api that calls my endpoint, passing an auth token in the header:
headers = {'content-type': 'application/json',
'Authorization': 'Token {}'.format(myToken)}
url = 'http://localhost:8000/my_endpoint/'
r = session.get(url=url, params=params, headers=headers)
In views.py, I have a method decorator that wraps the dispatch method on the view (viewsets.ReadOnlyModelViewSet):
def login_required(f):
def check_login_and_call(request, *args, **kwargs):
authentication = request.META.get('HTTP_AUTHORIZATION', b'')
if isinstance(authentication, str):
authentication = authentication.encode(HTTP_HEADER_ENCODING)
key = authentication.split()
if not key or len(key) != 2:
raise PermissionDenied('Authentication failed.')
user, token = authenticate_credentials(key[1])
return f(request, *args, **kwargs)
return check_login_and_call
I'm trying to write a test to authenticate the request using a token:
from rest_framework.authtoken.models import Token
from rest_framework.test import APIRequestFactory
from rest_framework.test import APITestCase
from rest_framework.test import force_authenticate
class EndpointViewTest(APITestCase):
def setUp(self):
self.factory = APIRequestFactory()
self.user = User.objects.create_user(
username='[email protected]', email='[email protected]', password='top_secret')
self.token = Token.objects.create(user=self.user)
self.token.save()
def test_token_auth(self):
request = self.factory.get('/my_endpoint')
force_authenticate(request, token=self.token.key)
view = views.EndpointViewSet.as_view({'get': 'list'})
response = view(request)
self.assertEqual(response.status_code, 200)
json_response = json.loads(response.render().content)['results']
For some reason, I cannot get the request to properly pass the token for this test. Using force_authenticate doesn't seem to change the header that I'm using for validating the token. The current output is raising "PermissionDenied: Authentication failed." because the token isn't being set on the request.
Is there a proper way to set this in the request header in my test or to refactor the way I'm using it in the first place?
Upvotes: 28
Views: 21491
Reputation: 856
The simpler way to force_authentication
using a built-in method from APITestCase
is:
class Test(APITestCase):
def setUp(self):
user1 = User.objects.create_user(username='foo')
self.client.force_authenticate(user=user1) # self.client is from APITestCase
... the rest of your tests ...
Upvotes: -1
Reputation: 2897
I wanted to test the authentication function itself, so forcing authentication wans't an option.
One way to properly pass the token is to use APIClient, which you already have imported.
client = APIClient()
client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key)
response = client.get('/api/vehicles/')
That sets your given token into the request header and lets the back end decide if it's valid or not.
Upvotes: 11
Reputation: 1020
Sorry for digging this old thread up, but if someone is using APIClient()
to do their tests you can do the following:
from rest_framework.test import APITestCase
from rest_framework.test import APIClient
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import User
class VehicleCreationTests(APITestCase):
def setUp(self):
self.client = APIClient()
self.user = User.objects.create_superuser('admin', '[email protected]', 'admin123')
self.token = Token.objects.create(user=self.user)
def testcase(self):
self.client.force_login(user=self.user)
response = self.client.post('/api/vehicles/', data=vehicle_data, format='json', HTTP_AUTHORIZATION=self.token)
self.assertEqual(response.status_code, 201)
Really good resource that I've used to come up with this is django-rest-framework-jwt tests
Upvotes: 7
Reputation: 9908
I found a way to get the test to pass, but please post if you have a better idea of how to handle any of this.
request = self.factory.get('/my_endpoint', HTTP_AUTHORIZATION='Token {}'.format(self.token))
force_authenticate(request, user=self.user)
After changing the above two lines of the test, it seems to authenticate based on the token properly.
Upvotes: 33