Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Added a default test Client to TestCase, and added some assertions fo…

…r some common testing patterns.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@5150 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit a0ef3ba2f7e815b4f3617f6e2827f66c13de3194 1 parent e986e8a
Russell Keith-Magee authored
32  django/test/testcases.py
... ...
@@ -1,8 +1,10 @@
1 1
 import re, doctest, unittest
  2
+from urlparse import urlparse
2 3
 from django.db import transaction
3 4
 from django.core import management
4 5
 from django.db.models import get_apps
5  
-    
  6
+from django.test.client import Client
  7
+
6 8
 normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
7 9
 
8 10
 class OutputChecker(doctest.OutputChecker):
@@ -46,5 +48,33 @@ def run(self, result=None):
46 48
         super().
47 49
         
48 50
         """
  51
+        self.client = Client()
49 52
         self.install_fixtures()
50 53
         super(TestCase, self).run(result)
  54
+
  55
+    def assertRedirects(self, response, expected_path):
  56
+        """Assert that a response redirected to a specific URL, and that the 
  57
+        redirect URL can be loaded.
  58
+        
  59
+        """
  60
+        self.assertEqual(response.status_code, 302, 
  61
+            "Response didn't redirect: Reponse code was %d" % response.status_code)
  62
+        scheme, netloc, path, params, query, fragment = urlparse(response['Location'])
  63
+        self.assertEqual(path, expected_path, 
  64
+            "Response redirected to '%s', expected '%s'" % (path, expected_path))
  65
+        redirect_response = self.client.get(path)
  66
+        self.assertEqual(redirect_response.status_code, 200, 
  67
+            "Couldn't retrieve redirection page '%s'" % path)
  68
+    
  69
+    def assertContains(self, response, text, count=1):
  70
+        """Assert that a response indicates that a page was retreived successfully,
  71
+        (i.e., the HTTP status code was 200), and that ``text`` occurs ``count`` 
  72
+        times in the content of the response.
  73
+        
  74
+        """
  75
+        self.assertEqual(response.status_code, 200,
  76
+            "Couldn't retrieve page'")
  77
+        real_count = response.content.count(text)
  78
+        self.assertEqual(real_count, count,
  79
+            "Could only find %d of %d instances of '%s' in response" % (real_count, count, text))
  80
+            
54  docs/testing.txt
@@ -166,7 +166,7 @@ To assist in testing various features of your application, Django provides
166 166
 tools that can be used to establish tests and test conditions.
167 167
 
168 168
 * `Test Client`_
169  
-* Fixtures_
  169
+* `TestCase`_
170 170
 
171 171
 Test Client
172 172
 -----------
@@ -357,9 +357,31 @@ The following is a simple unit test using the Test Client::
357 357
             # Check that the rendered context contains 5 customers
358 358
             self.failUnlessEqual(len(response.context['customers']), 5)
359 359
 
360  
-Fixtures
  360
+TestCase
361 361
 --------
362 362
 
  363
+Normal python unit tests extend a base class of ``unittest.testCase``. 
  364
+Django provides an extension of this base class - ``django.test.TestCase`` 
  365
+- that provides some additional capabilities that can be useful for 
  366
+testing web sites. 
  367
+
  368
+Moving from a normal unittest TestCase to a Django TestCase is easy - just
  369
+change the base class of your test from ``unittest.TestCase`` to 
  370
+``django.test.TestCase``. All of the standard Python unit test facilities
  371
+will continue to be available, but they will be augmented with some useful
  372
+extra facilities.
  373
+
  374
+Default Test Client
  375
+~~~~~~~~~~~~~~~~~~~
  376
+** New in Django development version **
  377
+
  378
+Every test case in a ``django.test.TestCase`` instance has access to an
  379
+instance of a Django `Test Client`_. This Client can be accessed as 
  380
+``self.client``. This client is recreated for each test.
  381
+
  382
+Fixture loading
  383
+~~~~~~~~~~~~~~~
  384
+
363 385
 A test case for a database-backed website isn't much use if there isn't any
364 386
 data in the database. To make it easy to put test data into the database,
365 387
 Django provides a fixtures framework.
@@ -376,16 +398,14 @@ multiple applications.
376 398
     This provides a mechanism to populate a new database with any initial
377 399
     data (such as a default set of categories). Fixtures with other names
378 400
     can be installed manually using ``django-admin.py loaddata``. 
379  
-    
380 401
 
381 402
 However, for the purposes of unit testing, each test must be able to 
382 403
 guarantee the contents of the database at the start of each and every
383  
-test. To do this, Django provides a TestCase baseclass that can integrate
384  
-with fixtures.
  404
+test. 
385 405
 
386  
-Moving from a normal unittest TestCase to a Django TestCase is easy - just
387  
-change the base class of your test, and define a list of fixtures
388  
-to be used. For example, the test case from `Writing unittests`_ would 
  406
+To define a fixture for a test, all you need to do is add a class
  407
+attribute to your test describing the fixtures you want the test to use.
  408
+For example, the test case from `Writing unittests`_ would 
389 409
 look like::
390 410
 
391 411
     from django.test import TestCase
@@ -410,6 +430,24 @@ This flush/load procedure is repeated for each test in the test case, so you
410 430
 can be certain that the outcome of a test will not be affected by 
411 431
 another test, or the order of test execution.
412 432
 
  433
+Assertions
  434
+~~~~~~~~~~
  435
+** New in Django development version **
  436
+
  437
+Normal Python unit tests have a wide range of assertions, such as 
  438
+``assertTrue`` and ``assertEquals`` that can be used to validate behavior. 
  439
+``django.TestCase`` adds to these, providing some assertions
  440
+that can be useful in testing the behavior of web sites.
  441
+
  442
+``assertRedirects(response, expected_path)``
  443
+    Assert that the response received redirects the browser to the provided
  444
+    path, and that the expected_path can be retrieved.
  445
+
  446
+``assertContains(response, text, count=1)``
  447
+    Assert that a response indicates that a page was retreived successfully,
  448
+    (i.e., the HTTP status code was 200), and that ``text`` occurs ``count`` 
  449
+    times in the content of the response.
  450
+    
413 451
 Running tests
414 452
 =============
415 453
 
13  tests/modeltests/test_client/models.py
@@ -24,19 +24,14 @@
24 24
 class ClientTest(TestCase):
25 25
     fixtures = ['testdata.json']
26 26
     
27  
-    def setUp(self):
28  
-        "Set up test environment"
29  
-        self.client = Client()
30  
-        
31 27
     def test_get_view(self):
32 28
         "GET a view"
33 29
         response = self.client.get('/test_client/get_view/')
34 30
         
35 31
         # Check some response details
36  
-        self.assertEqual(response.status_code, 200)
  32
+        self.assertContains(response, 'This is a test')
37 33
         self.assertEqual(response.context['var'], 42)
38 34
         self.assertEqual(response.template.name, 'GET Template')
39  
-        self.failUnless('This is a test.' in response.content)
40 35
 
41 36
     def test_get_post_view(self):
42 37
         "GET a view that normally expects POSTs"
@@ -80,7 +75,7 @@ def test_redirect(self):
80 75
         response = self.client.get('/test_client/redirect_view/')
81 76
         
82 77
         # Check that the response was a 302 (redirect)
83  
-        self.assertEqual(response.status_code, 302)
  78
+        self.assertRedirects(response, '/test_client/get_view/')
84 79
 
85 80
     def test_valid_form(self):
86 81
         "POST valid data to a form"
@@ -102,7 +97,7 @@ def test_incomplete_data_form(self):
102 97
             'value': 37            
103 98
         }
104 99
         response = self.client.post('/test_client/form_view/', post_data)
105  
-        self.assertEqual(response.status_code, 200)
  100
+        self.assertContains(response, 'This field is required', 3)
106 101
         self.assertEqual(response.template.name, "Invalid POST Template")
107 102
 
108 103
     def test_form_error(self):
@@ -130,7 +125,7 @@ def test_view_with_login(self):
130 125
         
131 126
         # Get the page without logging in. Should result in 302.
132 127
         response = self.client.get('/test_client/login_protected_view/')
133  
-        self.assertEqual(response.status_code, 302)
  128
+        self.assertRedirects(response, '/accounts/login/')
134 129
         
135 130
         # Request a page that requires a login
136 131
         response = self.client.login('/test_client/login_protected_view/', 'testclient', 'password')

0 notes on commit a0ef3ba

Please sign in to comment.
Something went wrong with that request. Please try again.