diff options
| author | Russell Keith-Magee <russell@keith-magee.com> | 2007-05-05 03:03:33 +0000 |
|---|---|---|
| committer | Russell Keith-Magee <russell@keith-magee.com> | 2007-05-05 03:03:33 +0000 |
| commit | a0ef3ba2f7e815b4f3617f6e2827f66c13de3194 (patch) | |
| tree | 5f2289eaef2d65c8c5bdc43b4632c57da2de342c | |
| parent | e986e8aba4d301c2be2b3a2607ee57e7de2fb494 (diff) | |
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
| -rw-r--r-- | django/test/testcases.py | 32 | ||||
| -rw-r--r-- | docs/testing.txt | 54 | ||||
| -rw-r--r-- | tests/modeltests/test_client/models.py | 13 |
3 files changed, 81 insertions, 18 deletions
diff --git a/django/test/testcases.py b/django/test/testcases.py index 2bfb9a733a..efe392590f 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -1,8 +1,10 @@ import re, doctest, unittest +from urlparse import urlparse from django.db import transaction from django.core import management 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) class OutputChecker(doctest.OutputChecker): @@ -46,5 +48,33 @@ class TestCase(unittest.TestCase): super(). """ + self.client = Client() self.install_fixtures() 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)) +
\ No newline at end of file diff --git a/docs/testing.txt b/docs/testing.txt index a135734144..5a2579b624 100644 --- a/docs/testing.txt +++ b/docs/testing.txt @@ -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. * `Test Client`_ -* Fixtures_ +* `TestCase`_ 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 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 data in the database. To make it easy to put test data into the database, Django provides a fixtures framework. @@ -376,16 +398,14 @@ multiple applications. This provides a mechanism to populate a new database with any initial data (such as a default set of categories). Fixtures with other names can be installed manually using ``django-admin.py loaddata``. - 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 -test. To do this, Django provides a TestCase baseclass that can integrate -with fixtures. +test. -Moving from a normal unittest TestCase to a Django TestCase is easy - just -change the base class of your test, and define a list of fixtures -to be used. For example, the test case from `Writing unittests`_ would +To define a fixture for a test, all you need to do is add a class +attribute to your test describing the fixtures you want the test to use. +For example, the test case from `Writing unittests`_ would look like:: 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 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 ============= diff --git a/tests/modeltests/test_client/models.py b/tests/modeltests/test_client/models.py index 44ddffb55f..58f1d47e50 100644 --- a/tests/modeltests/test_client/models.py +++ b/tests/modeltests/test_client/models.py @@ -24,19 +24,14 @@ from django.test import Client, TestCase class ClientTest(TestCase): fixtures = ['testdata.json'] - def setUp(self): - "Set up test environment" - self.client = Client() - def test_get_view(self): "GET a view" response = self.client.get('/test_client/get_view/') # 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.template.name, 'GET Template') - self.failUnless('This is a test.' in response.content) def test_get_post_view(self): "GET a view that normally expects POSTs" @@ -80,7 +75,7 @@ class ClientTest(TestCase): response = self.client.get('/test_client/redirect_view/') # 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): "POST valid data to a form" @@ -102,7 +97,7 @@ class ClientTest(TestCase): 'value': 37 } 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") def test_form_error(self): @@ -130,7 +125,7 @@ class ClientTest(TestCase): # Get the page without logging in. Should result in 302. 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 response = self.client.login('/test_client/login_protected_view/', 'testclient', 'password') |
