在Django测试框架中使用基本的HTTP访问身份验证

60

对于一些我在 Django 中的视图,我创建了一个装饰器来执行基本的 HTTP 访问身份验证。然而,在编写 Django 的测试用例时,我花了一些时间才弄清楚如何对该视图进行身份验证。以下是我是如何做到的。希望对某些人有所帮助。

6个回答

92

这是我如何做到的:

from django.test import Client
import base64
auth_headers = {
    'HTTP_AUTHORIZATION': 'Basic ' + base64.b64encode('username:password'),
}
c = Client()
response = c.get('/my-protected-url/', **auth_headers)

注意:您还需要创建一个用户。


11
如果您正在处理Unicode字符串(例如通过使用Python 3),则需要执行类似于 base64.b64encode('username:password'.encode()).decode() 的操作,因为base64模块不能处理Unicode字符串。 - Jocelyn delalande
@Jocelyndelalande 更像是 base64.b64encode(bytes('username:password', 'utf8')).decode('utf8') - Ramon Moraes
1
很棒的回答!只是补充一下:我在使用Python 3.5时,就像Django文档CGI文档所示,只需执行c.get('/my-protected-url/', HTTP_AUTHORIZATION='Basic username:password')即可。 - lucastamoios
3
为什么使用HTTP_AUTHORIZATION而不是Authorization? - Daniel Gomez Rico
1
@DanielGomezRico:确实令人困惑。有点晚了,但在https://dev59.com/RGUp5IYBdhLWcg3wtZE0中可以找到解释。 - djvg
显示剩余3条评论

34

在Django的测试用例中,您可以更新客户端默认值以包含您的HTTP基本身份验证凭据。

import base64
from django.test import TestCase

class TestMyStuff(TestCase):

    def setUp(self):
        credentials = base64.b64encode('username:password')
        self.client.defaults['HTTP_AUTHORIZATION'] = 'Basic ' + credentials

2
这也可以使用Client对象的credentials()方法更简洁地完成:self.client.credentials(HTTP_AUTHORIZATION='Basic ' + credentials)(参见http://www.django-rest-framework.org/api-guide/testing/#credentialskwargs)。 - Kurt Peek
@KurtPeek 这只适用于 Django REST Framework,而不是原始的 Django 测试客户端。 - Salvioner

8

对于Python3,您可以使用base64编码用户名:密码字符串:

base64.b64encode(b'username:password')

这将返回字节,因此您需要使用 .decode('ascii') 将其转换为 ASCII 字符串:

完整示例:

import base64

from django.test import TestCase

class TestClass(TestCase):
   def test_authorized(self):
       headers = {
           'HTTP_AUTHORIZATION': 'Basic ' + 
                base64.b64encode(b'username:password').decode("ascii")
       }
       response = self.client.get('/', **headers)
       self.assertEqual(response.status_code, 200)

感谢您的答案。它非常有帮助。 - Chetan Vashisth

2

假设我有一个登录表单,我使用以下技术通过测试框架进行登录:

    client = Client()
    client.post('/login/', {'username': 'john.smith', 'password': 'secret'})

我在其他测试中使用已经验证过的client。你对这篇文章有什么问题?

是的,那是一个不错的解决方案,Thierry。虽然它不能用于HTTP授权,因为它通过发送登录凭据作为HTTP头来工作。我这样做是因为视图是API调用而不是常规视图。 - Humphrey
这是我找到的唯一解决方案(适用于浏览器视图)。更加简单明了的方法是:Django客户端登录方法 - Beolap

2

(Python3) 我在测试中使用了以下代码:

credentials_string = '%s:%s' % ('invalid', 'invalid')
credentials = base64.b64encode(credentials_string.encode())
self.client.defaults['HTTP_AUTHORIZATION'] = 'Basic ' + credentials.decode()

并在视图中添加以下内容:

import base64
[...]
type, auth = request.META['HTTP_AUTHORIZATION'].split(' ', 1)
auth = base64.b64decode(auth.strip()).decode()

-1
另一种方法是绕过Django Client(),而是使用Requests来完成。
class MyTest(TestCase):
    def setUp(self):
        AUTH = requests.auth.HTTPBasicAuth("username", "password")

    def some_test(self):
        resp = requests.get(BASE_URL + 'endpoint/', auth=AUTH)
        self.assertEqual(resp.status_code, 200)

1
requests 的响应类并不能保证与 Django 使用的响应类完全兼容。例如,您将无法使用 response.context - fcurella
3
这将在网络上发起一个真实的 HTTP 请求。在单元测试中,这几乎永远不是您想要做的事情。 - slinkp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接