如何在Django测试POST接口时包含CSRF令牌?

38 投票
1 回答
16206 浏览
提问于 2025-04-18 15:05

我正在学习如何创建一个API接口,并且我想写一个测试,看看发送一个POST请求是否能返回200的状态码。我还打算写更多的测试,检查这个接口是否能返回所有预期的结果。不过,我总是得到403的状态码,我觉得这可能是因为我需要在POST数据中包含一个csrf令牌。请问在Django中测试一个POST接口的好方法是什么?

我的测试代码:

from django.test import TestCase
from app import settings
import requests

class ProjectEndpoint(TestCase):
   def post_endpoint(self):
      data = {'hello':'23'}
      post_project = requests.post(settings.BASE_URL+'/api/project', params=data)
      self.assertEqual(post_endpoint.status_code, 200)

这个测试总是失败,返回的是403而不是200。

我觉得这是因为这个视图是为了防止csrf攻击而受到保护,但我并不太确定。希望有人能给我一些建议。

1 个回答

48

其实,Django 默认情况下在测试时并不会强制进行 CSRF 检查,具体可以参考这个链接:https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#testing

CsrfViewMiddleware 通常会给测试视图函数带来很大麻烦,因为每次发送 POST 请求时都需要带上 CSRF 令牌。为了这个原因,Django 的测试用 HTTP 客户端进行了修改,设置了一个标志,让请求可以放宽对中间件和 csrf_protect 装饰器的限制,这样它们就不会拒绝请求了。在其他方面(比如发送 cookies 等),它们的表现是一样的。

如果你出于某种原因想让测试客户端执行 CSRF 检查,你可以创建一个强制执行 CSRF 检查的测试客户端实例:

from django.test import Client

csrf_client = Client(enforce_csrf_checks=True)

不过,这要求你使用 Django 的客户端,而不是 requests;据我所知,Django 并没有模拟或处理 requests... 所以当你运行这个单元测试时,实际上是直接访问真实的服务器。

另外,请注意,你应该给你的测试函数命名时以 test_ 开头。

比如像这样(通过 django manage.py test .ProjectEndpoint 运行时):

def test_post_endpoint(self):
   data = {'hello':'23'}
   c = Client() #above, from django.test import TestCase,Client
   #optional, but may be necessary for your configuration: c.login("username","password")
   response = c.post('/api/project',params=data)
   self.assertEqual(response.status_code, 200)

撰写回答