Django Api-Key с модульным тестированием

Я пытаюсь внедрить модульные тесты в существующий проект, существующий проект использует Api-ключи для доступа и аутентификации на конечных точках Api.

если я сделаю следующее через postman или командную строку:

curl --location --request GET 'http://127.0.0.1:8000/api/user_db' \
--header 'Authorization: Api-Key REDACTED' \
--header 'Content-Type: application/json' \
--data-raw '{
    "username" : "test@testing.local"
}'

Это вызовет следующую функцию представления и вернет данные пользователя с соответствующим oid (json-ответ) без ошибок.

from django.shortcuts import render
from rest_framework_api_key.permissions import HasAPIKey
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from user_api.classes.UserController import (
    GetBusinessUser, 
    CreateBusinessUser, 
    UpdateBusinessUser, 
    DeleteBusinesssUser
)
from celery.utils.log import get_task_logger
import environ


logger = get_task_logger(__name__)
env = environ.Env()


class ProcessUserRequest(APIView):
    permission_classes = [HasAPIKey |IsAuthenticated ]
    def get(self, request):
        logger.info("Get Business User Request Received")
        result = GetBusinessUser(request)
        return Response(result["result"], 
                        content_type='application/json charset=utf-8', 
                        status=result["statuscode"]
                

Дополнительно вызывается следующая сокращенная функция:

def GetBusinessUser(request) -> Dict[str, Union[str, int]]:
    logger.info(f"Processing Get Username Request: {request.data}")
    valid_serializer = ValidateGetBusinessUserFormSerializer(data=request.data)
    valid_serializer.is_valid(raise_exception=True)
    username = valid_serializer.validated_data['username']
    return BusinessUser.objects.filter(username=username).first()

Так как я хочу сделать юнит-тесты, чтобы обеспечить возможность проверки перед развертыванием, я реализовал следующее в файле tests.py модуля:

from rest_framework.test import APITestCase, APIClient
from rest_framework_api_key.models import APIKey
from user_api.classes.UserController import GetBusinessUser
from django.urls import reverse

# Class Method for GetBusinessUser (truncated)
# try except handling and other user checks removed for stack


class ProcessUserRequestTest(APITestCase):
    def setUp(self):
        self.client = APIClient()
        # have also tried: self.headers = {'HTTP_AUTHORIZATION': f'Api-Key {self.api_key.key}'}
        self.client.credentials(HTTP_AUTHORIZATION='Api-Key SomeApiKeyValue')
        self.url = reverse('business_user')
        self.valid_payload = {'username': 'test@testing.local'}
        self.invalid_payload = {'param1': '', 'param2': 'value2'}

    def test_get_business_user_request(self):
        # also tried based on above:
        #  response = self.client.get(self.url, **self.headers, format='json')
        response = self.client.get(self.url, data=self.valid_payload, format='json')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, GetBusinessUser(response.data).data)

Независимо от того, что я делаю, всегда возвращается следующее, так что, судя по тестированию, добавление заголовков аутентификации или использование client.credentials не работает с Authorization: Api-Key somekey в качестве заголовка?

creating test database for alias 'default'...
System check identified no issues (0 silenced).
{'detail': ErrorDetail(string='Authentication credentials were not provided.', code='not_authenticated')}
F
======================================================================
FAIL: test_get_business_user_request (user_api.tests.ProcessUserRequestTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "../truncated/tests.py", line 19, in in test_get_business_user_request
    self.assertEqual(response.status_code, 200)
    AssertionError: 403 != 200

----------------------------------------------------------------------
Ran 1 test in 0.018s

FAILED (failures=1)
Destroying test database for alias 'default'...

Сталкивались ли вы с этим раньше и есть ли рабочее решение, чтобы я мог создавать модульные тесты?

Я долго мучился с этим, но все же разобрался, ниже приведен пример, который сработал у меня.

_, key = APIKey.objects.create_key(name="test")
authorization = f"Api-Key {key}"
response = self.client.put(url, data, HTTP_AUTHORIZATION=authorization, format="json")

Теперь, в вашем случае, я думаю, это будет выглядеть так:

from rest_framework.test import APITestCase, APIClient
from rest_framework_api_key.models import APIKey
from user_api.classes.UserController import GetBusinessUser
from django.urls import reverse


class ProcessUserRequestTest(APITestCase):
    def setUp(self):
        self.client = APIClient()
        self.url = reverse('business_user')
        self.valid_payload = {'username': 'test@testing.local'}
        self.invalid_payload = {'param1': '', 'param2': 'value2'}

    def test_get_business_user_request(self):
         _, key = APIKey.objects.create_key(name="test")
        authorization = f"Api-Key {key}"
        response = self.client.get(self.url, data=self.valid_payload, HTTP_AUTHORIZATION=authorization, format='json')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, GetBusinessUser(response.data).data)

Я не тестировал ваш код, рассматриваю его как псевдокод. И, пожалуйста, дайте мне знать, если вы смогли заставить его работать, и тогда я смогу скорректировать свой псевдокод соответствующим образом.

"Будьте здоровы"

Я думаю, что вы должны проверить API-KEY вручную, но когда вы должны получить доступ к API, вы должны просто аутентифицировать существующего пользователя, которого нужно запросить.

force_authenticate(request, user=user)  # to docs
self.client.force_authenticate(user=self.user) # im using like this it works

обратитесь к https://www.django-rest-framework.org/api-guide/testing/

  • Принудительная аутентификация

обратите внимание на эту ссылку на оригинальную документацию DRF

Если вы используете pytest (чего ОП не делал), то вы можете создать фикстуру, чтобы сделать это проще:

@pytest.fixture()
def get_api_key_client(api_client):
    _, key = APIKey.objects.create_key(name="test")

    def _get_api_key_client():
        api_client.credentials(HTTP_AUTHORIZATION=f"Api-Key {key}")
        return api_client

    return _get_api_key_client

А затем в вашем тесте:

@pytest.mark.describe("Test")
@pytest.mark.it("test something")
def test_scrappy_log_delete_reserved(get_api_key_client):
    api_client = get_api_key_client()
    api_client.get(
        reverse("app:endpoint-name"),
    )

Я считаю, что это более простой способ тестирования с помощью Django

Вернуться на верх