Rest_Framework APIClient测试返回401 Unauthorized with Token Auth



我正在为我的rest_frameworkneneneba API编写一些测试,并使用令牌身份验证来保护它。我决定使用DRF的APIClient类来模拟来自用户浏览器的调用。

我可以通过点击身份验证端点从API获取令牌,但当我尝试使用这些令牌对其他端点的任何进一步请求进行身份验证时,我会返回一个401 Unauthorized错误,消息为"无效令牌"。

奇怪的是,我可以复制粘贴完全相同的令牌,并通过HTTPIE之类的东西向完全相同的端点发出成功的手动GET请求。。。

这是我的tests.py:

import json
from rest_framework import status
from rest_framework.test import APIClient
from rest_framework.test import APITestCase

class TestUser(object):
"""
A basic user class to simplify requests to the API
Tokens can be generated by authing as a user to /v1/auth/
"""
def __init__(self, token):
self.client = APIClient()
self.token = token
self.client.credentials(HTTP_AUTHORIZATION='Token ' + token)
def get(self, url):
print("Token: {0}".format(self.token))
res = self.client.get(url)
print('GET {0}: {1}'.format(url, res.data))
return res
def post(self, url, data):
res = self.client.post(url, data, format='json')
print('POST {0}: {1}'.format(url, res.data))
return res
def patch(self, url, data):
res = self.client.patch(url, data, json=data)
print('PATCH {0}: {1}'.format(url, res.data))
return res
def delete(self, url):
res = self.client.delete(url)
print('DELETE {0}: {1}'.format(url, res.data))
return res

# Grab new tokens every time we run our tests
auth_client = APIClient()
SUPERUSER = TestUser(auth_client.post('/v1/auth/', {'username': 'TestUser',
'password': 'password'}).data['token'])
ADMIN = TestUser(auth_client.post('/v1/auth/', {'username': 'TestUser4',
'password': 'password'}).data['token'])
MANAGER = TestUser(auth_client.post('/v1/auth/', {'username': 'TestUser2',
'password': 'password'}).data['token'])
EMPLOYEE = TestUser(auth_client.post('/v1/auth/', {'username': 'TestUser3',
'password': 'password'}).data['token'])

class AdminSiteCompanies(APITestCase):
def test_list_crud_permissions(self):
# GET
url = "/v1/admin_site/companies/"
self.assertEqual(SUPERUSER.get(url).status_code, status.HTTP_200_OK)
self.assertEqual(ADMIN.get(url).status_code, status.HTTP_200_OK)
self.assertEqual(MANAGER.get(url).status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(EMPLOYEE.get(url).status_code, status.HTTP_403_FORBIDDEN)

这是上面测试的控制台输出,显示从API收到了一个有效的令牌,就在我尝试在测试中使用它时,它吐回401之前:

Creating test database for alias 'default'...
Token: d579dbe4980d8ac451a462fc78cf38f789decddf
GET /v1/admin_site/companies/: {'detail': 'Invalid token.'}
Destroying test database for alias 'default'...

以下是我使用HTTPIE和上面的令牌成功进行手动GET请求的控制台输出:

D:ProjectsAPI-Server>http http://127.0.0.1:8000/v1/admin_site/companies/ "Authorization: Token d579dbe4980d8ac451a462fc78cf38f789decddf"
HTTP/1.0 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Date: Fri, 01 May 2015 05:43:59 GMT
Server: WSGIServer/0.2 CPython/3.4.3
Vary: Accept
X-Frame-Options: SAMEORIGIN
[
{
"address": "1234 Fake Street",
"id": 1,
"name": "FedEx",
"shift_type": "OE"
},
{
"address": "Bolivia",
"id": 2,
"name": "UPS",
"shift_type": "PS"
}
]

以下是我的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.authtoken',
'serverapp',
'rest_framework_swagger',
)
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'serverapp.middlewares.EmployeeMiddleware',
)
ROOT_URLCONF = 'shiftserver.urls'
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_FILTER_BACKENDS': (
'rest_framework.filters.DjangoFilterBackend',
)
}
# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}

这是我第一次为Django/rest_framework编写测试,所以我一直在努力遵循DRF关于测试和身份验证的文档。然而,无论我尝试什么,我仍然无法克服这个"无效令牌"问题。

当我向一位比我更精通DRF的朋友寻求帮助时,他被难住了,所以希望你们能揭示我们都缺少的东西。

我想通了!在TestCase类之外发布到API会碰到我在运行测试时碰巧正在运行的实际API服务器。我重构了AdminSiteCompanies(APITestCase)来设置测试数据、用户,并对这些用户进行身份验证,所有这些都在类的setUp(self):中

class AdminSiteCompanies(APITestCase):
def setUp(self):
# Create test Objects here
...snip...
# Create test Users here
# SuperUser
create_user('TestUser', 'password', 'testuser@test.com', True, False, False, co1lo1.id)
...snip...
# Grab new tokens every time we run our tests
# APIClient allows us to emulate calls from a browser
auth_client = APIClient()
# Authenticate our users
self.SUPERUSER = TestUser(auth_client.post('/v1/auth/', {'username': 'TestUser', 'password': 'password'})
.data['token'])
...snip...
def test_list_crud_permissions(self):
# GET
url = "/v1/admin_site/companies/"
self.assertEqual(self.SUPERUSER.get(url).status_code, status.HTTP_200_OK)
# ^ Now passes test
...snip...

最新更新