测试 Django REST Framework 时,为什么无法使用 APIClient.credentials() 使用令牌进行身份验证?

twi*_*ity 5 python django functional-testing django-testing django-rest-framework

我正在针对基本 API 编写功能(而非单元)测试,如下所示:

from decouple import config
from rest_framework.test import APIClient, APITestCase


class ObjectAPIResponseTest(APITestCase):
    base_url = 'http://localhost:8000/api/objects/'
    token = config('LOCAL_API_TOKEN') # token stored in local .env file
    authenticated_client = APIClient()

    def setUp(self):
        self.authenticated_client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)

    def test_list_object_reaches_api(self):
        real_response = self.authenticated_client.get(self.base_url)

        self.assertEqual(real_response.status_code, 200)
        self.assertEqual(real_response.headers['content-type'], 'application/json')
Run Code Online (Sandbox Code Playgroud)

测试失败:AssertionError: 401 != 200

使用curl成功测试请求后,我决定尝试requests使用Authorization标头加载,而不是使用Django REST Framework的APIClient

import requests

from decouple import config
from rest_framework.test import APIClient, APITestCase


class ObjectAPIResponseTest(APITestCase):
    base_url = 'http://localhost:8000/api/objects/'
    token = config('LOCAL_API_TOKEN') # token stored in local .env file

    authentication_header = {
        'Authorization': 'Token ' + token
    }

    def test_list_object_reaches_api(self):
        real_response = requests.get(self.base_url, headers=self.authentication_header)

        self.assertEqual(real_response.status_code, 200)
        self.assertEqual(real_response.headers['content-type'], 'application/json')
Run Code Online (Sandbox Code Playgroud)

这通过了,这让我想知道我是否以某种方式使用APIClient.credentials()不正确,尽管我所做的似乎基本上符合文档

有什么想法我做错了吗?

更新

受到 Hafnernuss 评论的启发,我想了解请求的行为方式,因此我检查了请求和请求APIClient的响应。APIClientrequests

尽管我明确地请求了其 URL,但它看起来APIClient实际上并没有命中在我的开发服务器上运行的 API 端点。如果我删除要求对请求进行身份验证的权限并通过 APIClient 发送请求:

client_response = self.authenticated_client.get(self.base_url)
print(client_response.data)
Run Code Online (Sandbox Code Playgroud)

我得到这个response.data

[]
Run Code Online (Sandbox Code Playgroud)

不过,我知道我的本地数据库中至少有一个对象可以通过该端点访问。如果我在测试上下文中创建一个新对象并运行请求,我会返回该对象:

[OrderedDict([('id', 3), ('property', 'foo')])]
Run Code Online (Sandbox Code Playgroud)

所以我认为我对 的幕后情况有一个根本性的误解APIClient。它是否以某种方式模拟了对我的本地开发服务器 URL 的请求,并从从测试运行程序的数据库中提取的人为端点发出请求?

Ben*_*ari 0

这是绕过 API 身份验证的替代解决方案:

事实上,为了禁用任何类型的 API 身份验证(令牌、基本等),您可以在您的之上使用以下装饰器test_method()

from unittest.mock import patch

from <Respetive-App>.<Respective-Views> import <Respective-View>

@patch.object(<Respective-View>, "permission_classes", [])
def test_post_rate(self, api_client):
    payload = dict(content_id=self.content.id, score=5)
    response = api_client.post(
        "/api/endpoint/",
        payload,
    )
    assert response.status_code == 201
Run Code Online (Sandbox Code Playgroud)