使用API​​RequestFactory测试基于令牌的身份验证的正确方法是什么?

pun*_*lly 10 python authentication django integration-testing django-rest-framework

对我的端点的查询工作正常(只要我传递一个有效的令牌),它返回我的响应数据的json表示.

服务api中的代码调用我的端点,在头中传递一个auth令牌:

headers = {'content-type': 'application/json',
           'Authorization': 'Token {}'.format(myToken)}
url = 'http://localhost:8000/my_endpoint/'
r = session.get(url=url, params=params, headers=headers)
Run Code Online (Sandbox Code Playgroud)

在views.py中,我有一个方法装饰器,它在视图上包装dispatch方法(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
Run Code Online (Sandbox Code Playgroud)

我正在尝试使用令牌编写测试来验证请求:

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='user@foo.com', email='user@foo.com', 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']
Run Code Online (Sandbox Code Playgroud)

出于某种原因,我无法获得正确传递此测试令牌的请求.使用force_authenticate似乎不会更改我用于验证令牌的标头.当前输出正在提高"PermissionDenied:身份验证失败".因为没有在请求上设置令牌.

是否有正确的方法在我的测试中的请求标头中设置它或重构我首先使用它的方式?

pun*_*lly 14

我找到了一种让测试通过的方法,但如果您对如何处理这些问题有更好的了解,请发帖.

request = self.factory.get('/my_endpoint', HTTP_AUTHORIZATION='Token {}'.format(self.token))
force_authenticate(request, user=self.user)
Run Code Online (Sandbox Code Playgroud)

在更改了上面两行测试之后,它似乎正确地根据令牌进行身份验证.


The*_*ist 8

我想测试身份验证功能本身,因此强制身份验证不是一种选择。

正确传递令牌的一种方法是使用您已经导入的APIClient

client = APIClient()
client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key)
response = client.get('/api/vehicles/')
Run Code Online (Sandbox Code Playgroud)

这将您给定的令牌设置到请求标头中,并让后端决定它是否有效。


Bor*_*cev 5

很抱歉挖掘这个旧线程,但如果有人APIClient()用来做他们的测试,你可以执行以下操作:

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', 'admin@admin.com', '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)
Run Code Online (Sandbox Code Playgroud)

我用来提出这个的非常好的资源是django-rest-framework-jwt 测试

  • `self.client = APIClient()` 这行不是必需的,因为你是从已经拥有它的 `APITestCase` 继承的。 (3认同)