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 J.W. Percival in his book Test-Driven Development with Python.