Testing

本文详细介绍REST框架提供的测试工具,包括APIRequestFactory、APIClient和RequestsClient等,涵盖创建请求、身份验证、CSRF验证及配置等方面,助力高效API测试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

测试 (Testing)

天有不测风云,码有祸福旦夕 (指明了测试在编程开发中的重要性)。—— Jacob Kaplan-Moss

REST framework 包含一些扩展 Django 现有测试框架的辅助类,并改进对 API 请求的支持。

APIRequestFactory

扩展 Django 的现有的 RequestFactory

创建测试请求 (Creating test requests)

APIRequestFactory 类支持与 Django 的标准 RequestFactory 类几乎相同的 API。这意味着标准的 .get().post().put().patch().delete().head().options() 方法都可用。

from rest_framework.test import APIRequestFactory

# 使用标准 RequestFactory API 创建表单 POST 请求
factory = APIRequestFactory()
request = factory.post('/notes/', {'title': 'new idea'})
使用 format 参数 (Using the format argument)

创建请求主体 (如 postputpatch) 方法包括 format 参数,这使得使用除 multipart 表单数据以外的内容类型生成请求变得容易。例如:

# 创建 JSON POST 请求
factory = APIRequestFactory()
request = factory.post('/notes/', {'title': 'new idea'}, format='json')

默认情况下,可用的格式是 'multipart''json'。为了与 Django 的现有的 RequestFactory 兼容,默认格式是 'multipart'

要支持更广泛的请求格式集,或更改默认格式,请参阅配置部分

显式编码请求主体 (Explicitly encoding the request body)

如果您需要显式编码请求主体,则可以通过设置 content_type 标志来完成。例如:

request = factory.post('/notes/', json.dumps({'title': 'new idea'}), content_type='application/json')
PUT 和 PATCH 与表单数据 (PUT and PATCH with form data)

Django 的 RequestFactory 和 REST framework 的 APIRequestFactory 之间值得注意的一个区别是 multipart 表单数据将被编码为除 .post() 以外的方法。

例如,使用 APIRequestFactory,您可以像这样做一个表单 PUT 请求:

factory = APIRequestFactory()
request = factory.put('/notes/547/', {'title': 'remember to email dave'})

使用 Django 的 RequestFactory,您需要自己显式编码数据:

from django.test.client import encode_multipart, RequestFactory

factory = RequestFactory()
data = {'title': 'remember to email dave'}
content = encode_multipart('BoUnDaRyStRiNg', data)
content_type = 'multipart/form-data; boundary=BoUnDaRyStRiNg'
request = factory.put('/notes/547/', content, content_type=content_type)

强制验证 (Forcing authentication)

当使用请求工厂直接测试视图时,能够直接验证请求通常很方便,而不必构造正确的验证凭证。

要强制验证请求,请使用 force_authenticate() 方法。

from rest_framework.test import force_authenticate

factory = APIRequestFactory()
user = User.objects.get(username='olivia')
view = AccountDetail.as_view()

# 对视图进行身份验证请求...
request = factory.get('/accounts/django-superstars/')
force_authenticate(request, user=user)
response = view(request)

该方法的签名是 force_authenticate(request, user=None, token=None)。在进行呼叫时,可以设置用户和令牌中的一个或两个。

例如,当使用令牌强行进行身份验证时,您可能会执行以下操作:

user = User.objects.get(username='olivia')
request = factory.get('/accounts/django-superstars/')
force_authenticate(request, user=user, token=user.auth_token)

注意force_authenticate 直接将 request.user 设置为内存中的 user 实例。如果要更新已保存的 user 状态的多个测试中重新使用相同的 user 实例,则可能需要在测试之间调用 refresh_from_db()


注意:使用 APIRequestFactory 时,返回的对象是 Django 的标准 HttpRequest,而不是 REST framework 的 Request 对象,只有在调用视图后才会生成。

这意味着直接在请求对象上设置属性可能并不总是具有您期望的效果。例如,直接设置 .token 将不起作用,并且直接设置 .user 仅在使用会话身份验证时才有效。

# 如果正在使用 `SessionAuthentication`,请求将只进行身份验证。
request = factory.get('/accounts/django-superstars/')
request.user = user
response = view(request)

强制 CSRF 验证 (Forcing CSRF validation)

默认情况下,使用 APIRequestFactory 创建的请求在传递给 REST framework 视图时不会应用 CSRF 验证。如果您需要显式启用 CSRF 验证,则可以通过在实例化工厂时设置 enforce_csrf_checks 标志来实现。

factory = APIRequestFactory(enforce_csrf_checks=True)

注意:值得注意的是,Django 的标准 RequestFactory 不需要包含此选项,因为当使用常规的 Django 时,CSRF 验证发生在中间件中,当直接测试视图时不会运行。当使用 REST framework 时,CSRF 验证发生在视图内,因此请求工厂需要禁用视图级 CSRF 检查。


APIClient

扩展 Django 现有的 Client

发出请求 (Making requests)

APIClient 类支持与 Django 的标准 Client 类相同的请求接口。这意味着标准的 .get().post().put().patch().delete().head().options() 方法都可用。例如:

from rest_framework.test import APIClient

client = APIClient()
client.post('/notes/', {'title': 'new idea'}, format='json')

要支持更广泛的请求格式集,或更改默认格式,请参阅配置部分

认证 (Authenticating)

.login(**kwargs)

login 方法的功能与 Django 的常规 Client 类完全相同。这允许您对任何包含 SessionAuthentication 的视图进行身份验证。

# 在登录会话的上下文中发出所有请求。
client = APIClient()
client.login(username='lauren', password='secret')

要退出,请照常调用注销方法。

# 登出
client.logout()

login 方法适用于测试使用会话身份验证的 API,例如包含 AJAX 与 API 交互的网站。

.credentials(**kwargs)

credentials 方法可用于设置标头,然后测试客户端将包含在所有后续请求中。

from rest_framework.authtoken.models import Token
from rest_framework.test import APIClient

# 在所有请求中包含适当的 `Authorization:` 标头。
token = Token.objects.get(user__username='lauren')
client = APIClient()
client.credentials(HTTP_AUTHORIZATION='Token ' + token.key)

请注意,第二次调用 credentials 会覆盖任何现有凭证。您可以通过调用不带参数的方法来取消设置任何现有凭据。

# 停止包含任何凭据
client.credentials()

credentials 方法适用于测试需要验证标头的 API,例如基本身份验证,OAuth1 和 OAuth2 身份验证以及简单令牌身份验证方案。

.force_authenticate(user=None, token=None)

有时您可能希望完全绕过身份验证,并强制测试客户端的所有请求被自动视为已通过身份验证。

如果您正在测试 API 但不希望构建有效的身份验证凭据以发出测试请求,则这可能是一个有用的快捷方式。

user = User.objects.get(username='lauren')
client = APIClient()
client.force_authenticate(user=user)

要取消对后续请求的身份验证,请调用 force_authenticate 将用户和/或令牌设置为 None

client.force_authenticate(user=None)

CSRF 验证 (CSRF validation)

默认情况下,使用 APIClient 时不应用 CSRF 验证。如果您需要显式启用 CSRF 验证,则可以通过在实例化客户端时设置 enforce_csrf_checks 标志来实现。

client = APIClient(enforce_csrf_checks=True)

通常,CSRF 验证将仅适用于任何会话身份验证视图。这意味着 CSRF 验证只有在客户端通过调用 login() 登录后才会发生。


RequestsClient

REST framework 还包含一个客户端,用于使用流行的 Python 库 requests 与应用程序进行交互。 这可能是有用的,如果:

  • 您期望主要从另一个 Python 服务与 API 进行交互,并且希望在与客户端看到的相同级别上测试该服务。
  • 您希望以这样的方式编写测试,即它们也可以针对临时或实时环境运行。(请参阅下面的 “实时测试”。)

这公开了与直接使用请求会话完全相同的接口。

client = RequestsClient()
response = client.get('http://testserver/users/')
assert response.status_code == 200

请注意,请求客户端要求您传递完全限定的 URL。

RequestsClient 和使用数据库 (RequestsClient and working with the database)

如果您想编写仅与服务接口交互的测试,则 RequestsClient 类很有用。这比使用标准的 Django 测试客户端要严格一些,因为这意味着所有的交互必须通过 API。

如果您正在使用 RequestsClient,则需要确保测试设置和结果断言作为常规 API 调用执行,而不是直接与数据库模型交互。例如,与其检查 Customer.objects.count() == 3,不如列出客户端点,并确保它包含三个记录。

标头和身份验证 (Headers & Authentication)

当使用标准的 requests.Session 实例以相同的方式提供自定义标头和身份验证凭据。

from requests.auth import HTTPBasicAuth

client.auth = HTTPBasicAuth('user', 'pass')
client.headers.update({'x-test': 'true'})

CSRF

如果您正在使用 SessionAuthentication,则需要为 POSTPUTPATCHDELETE 请求包含 CSRF 令牌。

您可以通过遵循基于 JavaScript 的客户端使用的相同流程来执行此操作。首先发出 GET 请求以获取 CRSF 令牌,然后在以下请求中显示该令牌。

例如:

client = RequestsClient()

# 获取 CSRF 令牌。
response = client.get('/homepage/')
assert response.status_code == 200
csrftoken = response.cookies['csrftoken']

# 与 API 交互。
response = client.post('/organisations/', json={
    'name': 'MegaCorp',
    'status': 'active'
}, headers={'X-CSRFToken': csrftoken})
assert response.status_code == 200

实时测试 (Live tests)

通过谨慎使用,RequestsClientCoreAPIClient 都提供了编写测试用例的能力,这些测试用例可以在开发中运行,也可以直接针对登台服务器或生产环境运行。

使用此样式创建一些核心功能的基本测试是验证实时服务的有效方法。这样做可能需要仔细注意安装和卸载,以确保测试以不直接影响客户数据的方式运行。


CoreAPIClient

CoreAPIClient 允许您使用 Python coreapi 客户端库与您的 API 进行交互。

# Fetch the API schema
client = CoreAPIClient()
schema = client.get('http://testserver/schema/')

# Create a new organisation
params = {'name': 'MegaCorp', 'status': 'active'}
client.action(schema, ['organisations', 'create'], params)

# Ensure that the organisation exists in the listing
data = client.action(schema, ['organisations', 'list'])
assert(len(data) == 1)
assert(data == [{'name': 'MegaCorp', 'status': 'active'}])

标头和身份验证 (Headers & Authentication)

自定义标头和身份验证可以与 CoreAPIClient 以类似于 RequestsClient 的方式一起使用。

from requests.auth import HTTPBasicAuth

client = CoreAPIClient()
client.session.auth = HTTPBasicAuth('user', 'pass')
client.session.headers.update({'x-test': 'true'})

API 测试用例 (API Test cases)

REST framework 包含以下测试用例类,它们镜像现有的 Django 测试用例类,但使用 API​​Client 而不是 Django 的默认 Client

  • APISimpleTestCase
  • APITransactionTestCase
  • APITestCase
  • APILiveServerTestCase

举个栗子

您可以像使用常规 Django 测试用例类一样使用任何 REST framework 的测试用例类。self.client 属性将是 APIClient 实例。

from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from myproject.apps.core.models import Account

class AccountTests(APITestCase):
    def test_create_account(self):
        """
        确保我们可以创建新的帐户对象。
        """
        url = reverse('account-list')
        data = {'name': 'DabApps'}
        response = self.client.post(url, data, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(Account.objects.count(), 1)
        self.assertEqual(Account.objects.get().name, 'DabApps')

URLPatternsTestCase

REST framework 还提供了一个测试用例类,用于在每个类的基础上隔离 urlpatterns。请注意,这继承自 Django 的 SimpleTestCase,并且很可能需要与另一个测试用例类混合使用。

举个栗子

from django.urls import include, path, reverse
from rest_framework.test import APITestCase, URLPatternsTestCase


class AccountTests(APITestCase, URLPatternsTestCase):
    urlpatterns = [
        path('api/', include('api.urls')),
    ]

    def test_create_account(self):
        """
        确保我们可以创建新的帐户对象。
        """
        url = reverse('account-list')
        response = self.client.get(url, format='json')
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data), 1)

测试响应 (Testing responses)

检查响应数据 (Checking the response data)

当检查测试响应的有效性时,检查创建响应的数据通常更方便,而不是检查完全渲染的响应。

例如,检查 response.data 更容易:

response = self.client.get('/users/4/')
self.assertEqual(response.data, {'id': 4, 'username': 'lauren'})

而不是检查解析 response.content 的结果:

response = self.client.get('/users/4/')
self.assertEqual(json.loads(response.content), {'id': 4, 'username': 'lauren'})

渲染响应 (Rendering responses)

如果您使用 API​​RequestFactory 直接测试视图,则返回的响应将不会渲染,因为模板响应的渲染由 Django 的内部请求 - 响应循环执行。为了访问 response.content,您首先需要渲染响应。

view = UserDetail.as_view()
request = factory.get('/users/4')
response = view(request, pk='4')
response.render()  # 没有这个,无法访问`response.content`。
self.assertEqual(response.content, '{"username": "lauren", "id": 4}')

配置 (Configuration)

设置默认格式 (Setting the default format)

可以使用 TEST_REQUEST_DEFAULT_FORMAT 设置键设置用于发出测试请求的默认格式。例如,要始终对测试请求默认使用 JSON 而不是标准的 multipart 表单请求,请在 settings.py 文件中设置以下内容:

REST_FRAMEWORK = {
    ...
    'TEST_REQUEST_DEFAULT_FORMAT': 'json'
}

设置可用的格式 (Setting the available formats)

如果需要使用除 multipart 或 json 请求之外的其他东西来测试请求,可以通过设置TEST_REQUEST_RENDERER_CLASSES 设置来完成。

例如,要在测试请求中添加对 format ='html' 的支持,您可能在 settings.py 文件中有类似的内容。

REST_FRAMEWORK = {
    ...
    'TEST_REQUEST_RENDERER_CLASSES': (
        'rest_framework.renderers.MultiPartRenderer',
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.TemplateHTMLRenderer'
    )
}
Among the tests you perform on web applications, security testing is perhaps the most important, yet it's often the most neglected. The recipes in the Web Security Testing Cookbook demonstrate how developers and testers can check for the most common web security issues, while conducting unit tests, regression tests, or exploratory tests. Unlike ad hoc security assessments, these recipes are repeatable, concise, and systematic-perfect for integrating into your regular test suite. Recipes cover the basics from observing messages between clients and servers to multi-phase tests that script the login and execution of web application features. By the end of the book, you'll be able to build tests pinpointed at Ajax functions, as well as large multi-step tests for the usual suspects: cross-site scripting and injection attacks. This book helps you: Obtain, install, and configure useful-and free-security testing tools Understand how your application communicates with users, so you can better simulate attacks in your tests Choose from many different methods that simulate common attacks such as SQL injection, cross-site scripting, and manipulating hidden form fields Make your tests repeatable by using the scripts and examples in the recipes as starting points for automated tests Don't live in dread of the midnight phone call telling you that your site has been hacked. With Web Security Testing Cookbook and the free tools used in the book's examples, you can incorporate security coverage into your test suite, and sleep in peace.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值