how do you include a csrf token when testing a POST endpoint in django?

Question:

I am learning how to make an api endpoint and I am trying to write a test to see if a post request returns a 200 status code. I plan on writing more tests to see if the endpoint is returning all the expected results as well. I keep getting a 403 status code and I think it is because I need to include a csrf token in the post data. What is a good way to test a POST endpoint in django?

my Test:

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)

This test keeps failing with 403 != 200

I think it is because the view is protected against csrf attacks, but I am really not sure. Appreciate any insight that someone has.

Asked By: Spencer Cooley

||

Answers:

Actually, django doesn’t enforce (by default) csrf checking with tests, as per https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#testing:

The CsrfViewMiddleware will usually be a big hindrance to testing view
functions, due to the need for the CSRF token which must be sent with
every POST request. For this reason, Django’s HTTP client for tests
has been modified to set a flag on requests which relaxes the
middleware and the csrf_protect decorator so that they no longer
rejects requests. In every other respect (e.g. sending cookies etc.),
they behave the same.

If, for some reason, you want the test client to perform CSRF checks,
you can create an instance of the test client that enforces CSRF
checks:

from django.test import Client

csrf_client = Client(enforce_csrf_checks=True)

However, this does require you to be using the Django Client vs requests; as far as I know, Django doesn’t mock/instrument/etc. requests… so you’re actually hitting the real server when you run that unit test.

Also note that you should name your test functions something that starts with test_

So something like this (when run through 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)
Answered By: Foon
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.