Added a default test Client to TestCase, and added some assertions for some common testing patterns.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@5150 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2007-05-05 03:03:33 +00:00
parent e986e8aba4
commit a0ef3ba2f7
3 changed files with 81 additions and 18 deletions

View File

@ -1,8 +1,10 @@
import re, doctest, unittest import re, doctest, unittest
from urlparse import urlparse
from django.db import transaction from django.db import transaction
from django.core import management from django.core import management
from django.db.models import get_apps from django.db.models import get_apps
from django.test.client import Client
normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s) normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
class OutputChecker(doctest.OutputChecker): class OutputChecker(doctest.OutputChecker):
@ -46,5 +48,33 @@ class TestCase(unittest.TestCase):
super(). super().
""" """
self.client = Client()
self.install_fixtures() self.install_fixtures()
super(TestCase, self).run(result) super(TestCase, self).run(result)
def assertRedirects(self, response, expected_path):
"""Assert that a response redirected to a specific URL, and that the
redirect URL can be loaded.
"""
self.assertEqual(response.status_code, 302,
"Response didn't redirect: Reponse code was %d" % response.status_code)
scheme, netloc, path, params, query, fragment = urlparse(response['Location'])
self.assertEqual(path, expected_path,
"Response redirected to '%s', expected '%s'" % (path, expected_path))
redirect_response = self.client.get(path)
self.assertEqual(redirect_response.status_code, 200,
"Couldn't retrieve redirection page '%s'" % path)
def assertContains(self, response, text, count=1):
"""Assert that a response indicates that a page was retreived successfully,
(i.e., the HTTP status code was 200), and that ``text`` occurs ``count``
times in the content of the response.
"""
self.assertEqual(response.status_code, 200,
"Couldn't retrieve page'")
real_count = response.content.count(text)
self.assertEqual(real_count, count,
"Could only find %d of %d instances of '%s' in response" % (real_count, count, text))

View File

@ -166,7 +166,7 @@ To assist in testing various features of your application, Django provides
tools that can be used to establish tests and test conditions. tools that can be used to establish tests and test conditions.
* `Test Client`_ * `Test Client`_
* Fixtures_ * `TestCase`_
Test Client Test Client
----------- -----------
@ -357,9 +357,31 @@ The following is a simple unit test using the Test Client::
# Check that the rendered context contains 5 customers # Check that the rendered context contains 5 customers
self.failUnlessEqual(len(response.context['customers']), 5) self.failUnlessEqual(len(response.context['customers']), 5)
Fixtures TestCase
-------- --------
Normal python unit tests extend a base class of ``unittest.testCase``.
Django provides an extension of this base class - ``django.test.TestCase``
- that provides some additional capabilities that can be useful for
testing web sites.
Moving from a normal unittest TestCase to a Django TestCase is easy - just
change the base class of your test from ``unittest.TestCase`` to
``django.test.TestCase``. All of the standard Python unit test facilities
will continue to be available, but they will be augmented with some useful
extra facilities.
Default Test Client
~~~~~~~~~~~~~~~~~~~
** New in Django development version **
Every test case in a ``django.test.TestCase`` instance has access to an
instance of a Django `Test Client`_. This Client can be accessed as
``self.client``. This client is recreated for each test.
Fixture loading
~~~~~~~~~~~~~~~
A test case for a database-backed website isn't much use if there isn't any A test case for a database-backed website isn't much use if there isn't any
data in the database. To make it easy to put test data into the database, data in the database. To make it easy to put test data into the database,
Django provides a fixtures framework. Django provides a fixtures framework.
@ -376,16 +398,14 @@ multiple applications.
This provides a mechanism to populate a new database with any initial This provides a mechanism to populate a new database with any initial
data (such as a default set of categories). Fixtures with other names data (such as a default set of categories). Fixtures with other names
can be installed manually using ``django-admin.py loaddata``. can be installed manually using ``django-admin.py loaddata``.
However, for the purposes of unit testing, each test must be able to However, for the purposes of unit testing, each test must be able to
guarantee the contents of the database at the start of each and every guarantee the contents of the database at the start of each and every
test. To do this, Django provides a TestCase baseclass that can integrate test.
with fixtures.
Moving from a normal unittest TestCase to a Django TestCase is easy - just To define a fixture for a test, all you need to do is add a class
change the base class of your test, and define a list of fixtures attribute to your test describing the fixtures you want the test to use.
to be used. For example, the test case from `Writing unittests`_ would For example, the test case from `Writing unittests`_ would
look like:: look like::
from django.test import TestCase from django.test import TestCase
@ -410,6 +430,24 @@ This flush/load procedure is repeated for each test in the test case, so you
can be certain that the outcome of a test will not be affected by can be certain that the outcome of a test will not be affected by
another test, or the order of test execution. another test, or the order of test execution.
Assertions
~~~~~~~~~~
** New in Django development version **
Normal Python unit tests have a wide range of assertions, such as
``assertTrue`` and ``assertEquals`` that can be used to validate behavior.
``django.TestCase`` adds to these, providing some assertions
that can be useful in testing the behavior of web sites.
``assertRedirects(response, expected_path)``
Assert that the response received redirects the browser to the provided
path, and that the expected_path can be retrieved.
``assertContains(response, text, count=1)``
Assert that a response indicates that a page was retreived successfully,
(i.e., the HTTP status code was 200), and that ``text`` occurs ``count``
times in the content of the response.
Running tests Running tests
============= =============

View File

@ -24,19 +24,14 @@ from django.test import Client, TestCase
class ClientTest(TestCase): class ClientTest(TestCase):
fixtures = ['testdata.json'] fixtures = ['testdata.json']
def setUp(self):
"Set up test environment"
self.client = Client()
def test_get_view(self): def test_get_view(self):
"GET a view" "GET a view"
response = self.client.get('/test_client/get_view/') response = self.client.get('/test_client/get_view/')
# Check some response details # Check some response details
self.assertEqual(response.status_code, 200) self.assertContains(response, 'This is a test')
self.assertEqual(response.context['var'], 42) self.assertEqual(response.context['var'], 42)
self.assertEqual(response.template.name, 'GET Template') self.assertEqual(response.template.name, 'GET Template')
self.failUnless('This is a test.' in response.content)
def test_get_post_view(self): def test_get_post_view(self):
"GET a view that normally expects POSTs" "GET a view that normally expects POSTs"
@ -80,7 +75,7 @@ class ClientTest(TestCase):
response = self.client.get('/test_client/redirect_view/') response = self.client.get('/test_client/redirect_view/')
# Check that the response was a 302 (redirect) # Check that the response was a 302 (redirect)
self.assertEqual(response.status_code, 302) self.assertRedirects(response, '/test_client/get_view/')
def test_valid_form(self): def test_valid_form(self):
"POST valid data to a form" "POST valid data to a form"
@ -102,7 +97,7 @@ class ClientTest(TestCase):
'value': 37 'value': 37
} }
response = self.client.post('/test_client/form_view/', post_data) response = self.client.post('/test_client/form_view/', post_data)
self.assertEqual(response.status_code, 200) self.assertContains(response, 'This field is required', 3)
self.assertEqual(response.template.name, "Invalid POST Template") self.assertEqual(response.template.name, "Invalid POST Template")
def test_form_error(self): def test_form_error(self):
@ -130,7 +125,7 @@ class ClientTest(TestCase):
# Get the page without logging in. Should result in 302. # Get the page without logging in. Should result in 302.
response = self.client.get('/test_client/login_protected_view/') response = self.client.get('/test_client/login_protected_view/')
self.assertEqual(response.status_code, 302) self.assertRedirects(response, '/accounts/login/')
# Request a page that requires a login # Request a page that requires a login
response = self.client.login('/test_client/login_protected_view/', 'testclient', 'password') response = self.client.login('/test_client/login_protected_view/', 'testclient', 'password')