简体   繁体   中英

Django test Client submitting a form with a POST request

How can I submit a POST request with Django test Client, such that I include form data in it? In particular, I would like to have something like (inspired by How should I write tests for Forms in Django? ):

from django.tests import TestCase

class MyTests(TestCase):
    def test_forms(self):
        response = self.client.post("/my/form/", {'something':'something'})

My endpoint /my/form has some internal logic to deal with 'something'. The problem was that when trying to later access request.POST.get('something') I couldn't get anything. I found a solution so I'm sharing below.

The key was to add content_type to the post method of client, and also urlencode the data.

from urllib import urlencode

...

data = urlencode({"something": "something"})
response = self.client.post("/my/form/", data, content_type="application/x-www-form-urlencoded")

Hope this helps someone!

If you are sending dictionaries on old-django versions using client you must define content_type='application/json' because its internal transformation fail to process dictionaries, also need send dictionary like a blob using json.dumps method, in conclusiont the next must works

import json
from django.tests import TestCase

class MyTests(TestCase):
    def test_forms(self):
        response = self.client.post("/my/form/", json.dumps({'something':'something'}), content_type='application/json')

If you provide content_type as application/json , the data is serialized using json.dumps() if it's a dict, list, or tuple. Serialization is performed with DjangoJSONEncoder by default, and can be overridden by providing a json_encoder argument to Client . This serialization also happens for put(), patch(), and delete() requests.

I have tried unit testing the POST requests in Django using Client() , but I fail to make it work (even with the methods specified above). So here is an alternative approach I take exclusively for the POST requests (using HttpRequest() ):

from django.http import HttpRequest
from django.tests import TestCase
from . import views
# If a different test directory is being used to store the test files, replace the dot with the app name

class MyTests(TestCase):
    def test_forms(self):
        request = HttpRequest()
        request.method = 'POST'
        request.POST['something'] = 'something'
        request.META['HTTP_HOST'] = 'localhost'
        response = views.view_function_name(request)
        self.assertNotIn(b'Form error message', response.content)
        # make more assertions, if needed

Replace the view_function_name() with the actual function name. This function sends a POST request to the view being tested with the form-field 'something' and it's corresponding value. The assertion statements would totally depend on the utility of the test functions, however. Here are some assertions that may be used:

  • self.assertEquals(response.status_code, 302) : Make this assertion when the form, upon submission of the POST request, redirects (302 is the status code for redirection). Read more about it here .
  • self.assertNotIn(b'Form error message', response.content) : Replace 'Form error message' with the error message that the form generates when incorrect details are sent through the request. The test would fail if the test data is incorrect (the text is converted to bytes since HttpResponse().content is a bytes object as well).

If the view function uses the Django Message framework for displaying the form error messages as well, include this before the response :

from django.contrib import messages
...
request._messages = messages.storage.default_storage(request)

If the view function uses Sessions, include this before the response :

from importlib import import_module
from django.conf import settings
...
engine = import_module(settings.SESSION_ENGINE)
session_key = None
request.session = engine.SessionStore(session_key)

Before sending out the request, remember the use of any context-processors that your application may use.

I personally find this method more intuitive (and functional). This seems to cover all possible test cases with regard to HTTP requests and forms as well.

I would also like to suggest that each unit test case could be broken down into separate components for increased coverage and discovering latent bugs in code, instead of clubbing all cases in a single test_forms() .

This technique was mentioned by Harry JW Percival in his book Test-Driven Development with Python .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM