1

After recently discovering (and loving!) test driven development, I've started working on an API for a project of mine using Django with Django REST Framework and Django REST Framework API Key. However, I'm getting stuck trying to unit test some basic API functionality: every time I attempt to do a GET request to the API, it returns a 403: "Authentication credentials were not provided."

Which is odd; I am adding the credentials in the header of the request before I send it off. When I print out the header of the RequestsClient, I am getting this result:

{'User-Agent': 'python-requests/2.25.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'X-Api-Key': 'ADtIa4s9.joEWAwZH47uowolz60c1hYnskVujO8lQ'}

Should work... yet for some reason my server (Django's internal server, running on localhost:8000) will not see it. I've been going through a ton of Questions on here and I am left completely baffled. Can you help me out a bit? Thanks in advance.

settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'rest_framework_api_key',
    'core',
    'base',
    'api',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework_api_key.permissions.HasAPIKey',
    ]
}
API_KEY_CUSTOM_HEADER = "HTTP_X_API_KEY"

urls.py

from django.contrib import admin
from django.urls import path
from rest_framework import routers
from api.viewsets import RestaurantViewSet

router = routers.SimpleRouter()
router.register(r'restaurant', RestaurantViewSet)

urlpatterns = [
    path('admin/', admin.site.urls),
]
urlpatterns += router.urls

api/viewsets.py

from rest_framework import viewsets
from .serializers import RestaurantSerializer
from base.models import Restaurant

class RestaurantViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Restaurant.objects.all()
    serializer_class = RestaurantSerializer

base/tests.py

class RestaurantTest(APITestCase):
    @classmethod
    def setUpTestData(cls):
        manager = User.objects.create(username="TestUser")
        cls.restaurant = Restaurant.objects.create(name="Testaurant", manager=manager)
        client_key = cls.restaurant.create_api_key()

        ...

        cls.client = RequestsClient()
        header = {'X-Api-Key': 'Api-Key {}'.format(client_key)}
        cls.client.headers.update(header)
        print(header)  # Output: see description above

    # This test passes
    def test_resolves_to_correct_view(self):
        found = resolve('/restaurant/')
        self.assertEqual(found.func.__name__, RestaurantViewSet.__name__)

    # This test fails with "Authentication credentials were not provided."
    def test_able_to_get(self):
        path = '/restaurant/'
        response = self.client.get(path)
        self.assertEqual(response.status_code, 200, msg="Request to '{}' failed: {} - {}".format(path, response.status_code, response.data['detail']))
  • 1
    sending a POST request to a non-detail `ModelViewSet` endpoint, will invoke the `create` action, only the `ReadOnlyModelViewSet` does not provide such an action. could you try with a `ModelViewSet` instead? – Igor Moraru Jun 10 '21 at 14:25
  • Unfortunately, that did not do the trick. It's supposed to be a GET request anyway, not a POST. But I'll keep that in mind once I get to those. Thank you. – Declan Ashmore Jun 10 '21 at 14:42
  • use `restaurant-list` for GET and `restaurant-detail` for update and delete post-request. Also, to make the response to work you need three parameters. 1- the url, 2-data, 3- the json format/headers. You should also add a `app_name=yourappname` into the url and a `basename='restaurante'`. There are a plenty of work for you to do testing your endpoints. – Elias Prado Jun 10 '21 at 14:45

0 Answers0