'When testing Django REST Framework, why can't I get APIClient.credentials() to authenticate using a token?
I'm writing a functional (not unit) test against a basic API, like so:
from decouple import config
from rest_framework.test import APIClient, APITestCase
class ObjectAPIResponseTest(APITestCase):
base_url = 'http://localhost:8000/api/objects/'
token = config('LOCAL_API_TOKEN') # token stored in local .env file
authenticated_client = APIClient()
def setUp(self):
self.authenticated_client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
def test_list_object_reaches_api(self):
real_response = self.authenticated_client.get(self.base_url)
self.assertEqual(real_response.status_code, 200)
self.assertEqual(real_response.headers['content-type'], 'application/json')
The test fails: AssertionError: 401 != 200
After successfully testing the request with curl, I decided to try to load up requests
with the Authorization
header instead of using Django REST Framework's APIClient
:
import requests
from decouple import config
from rest_framework.test import APIClient, APITestCase
class ObjectAPIResponseTest(APITestCase):
base_url = 'http://localhost:8000/api/objects/'
token = config('LOCAL_API_TOKEN') # token stored in local .env file
authentication_header = {
'Authorization': 'Token ' + token
}
def test_list_object_reaches_api(self):
real_response = requests.get(self.base_url, headers=self.authentication_header)
self.assertEqual(real_response.status_code, 200)
self.assertEqual(real_response.headers['content-type'], 'application/json')
This passes, which makes me wonder if I'm somehow using APIClient.credentials()
incorrectly, although what I've done seems basically in line with the documentation.
Any ideas what I'm doing wrong?
UPDATE
Inspired by Hafnernuss's comment, I wanted to see how the APIClient
request was behaving, so I inspected the response from both the APIClient
request and the requests
request.
It looks like APIClient
isn't actually hitting the API endpoint running on my development server, despite my explicitly requesting against its URL. If I remove the permission requiring a request to be authenticated and send a request through APIClient:
client_response = self.authenticated_client.get(self.base_url)
print(client_response.data)
I get this as response.data
:
[]
I know that there's at least one object in my local database accessible via that endpoint, though. If I create a new object in the context of the test and run the request, I get that object returned:
[OrderedDict([('id', 3), ('property', 'foo')])]
So I think I have a fundamental misunderstanding of what's going on under the hood with APIClient
. Is it somehow mocking out the request to my local development server's URL and requesting from a contrived endpoint that pulls from the test runner's database?
Solution 1:[1]
Here's an alternative solution to bypass API authentication:
In fact, in order to disable any kind of API auth (Token, Basic, etc) simply you can use the following decorator on top of your test_method()
:
from unittest.mock import patch
from <Respetive-App>.<Respective-Views> import <Respective-View>
@patch.object(<Respective-View>, "permission_classes", [])
def test_post_rate(self, api_client):
payload = dict(content_id=self.content.id, score=5)
response = api_client.post(
"/api/endpoint/",
payload,
)
assert response.status_code == 201
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |