Ticket #8138: 8138withdocs.diff
File 8138withdocs.diff, 25.0 KB (added by , 16 years ago) |
---|
-
django/test/simple.py
3 3 from django.db.models import get_app, get_apps 4 4 from django.test import _doctest as doctest 5 5 from django.test.utils import setup_test_environment, teardown_test_environment 6 from django.test.testcases import OutputChecker, DocTestRunner 6 from django.test.testcases import OutputChecker, DocTestRunner, TestCase 7 7 8 8 # The module name for tests outside models.py 9 9 TEST_MODULE = 'tests' … … 99 99 else: # label is app.TestClass.test_method 100 100 return TestClass(parts[2]) 101 101 102 def partition_suite(suite, classes, bins): 103 """ 104 Partitions a test suite by test type. 105 106 classes is a sequence of types 107 bins is a sequence of TestSuites, one more than classes 108 109 Tests of type classes[i] are added to bins[i], 110 tests with no match found in classes are place in bins[-1] 111 """ 112 for test in suite: 113 if isinstance(test, unittest.TestSuite): 114 partition_suite(test, classes, bins) 115 else: 116 for i in range(len(classes)): 117 if isinstance(test, classes[i]): 118 bins[i].addTest(test) 119 break 120 else: 121 bins[-1].addTest(test) 122 123 def reorder_suite(suite, classes): 124 """ 125 Reorders a test suite by test type. 126 127 classes is a sequence of types 128 129 All tests of type clases[0] are placed first, then tests of type classes[1], etc. 130 Tests with no match in classes are placed last. 131 """ 132 class_count = len(classes) 133 bins = [unittest.TestSuite() for i in range(class_count+1)] 134 partition_suite(suite, classes, bins) 135 for i in range(class_count): 136 bins[0].addTests(bins[i+1]) 137 return bins[0] 138 102 139 def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[]): 103 140 """ 104 141 Run the unit tests for all the test labels in the provided list. … … 137 174 for test in extra_tests: 138 175 suite.addTest(test) 139 176 177 suite = reorder_suite(suite, (TestCase,)) 178 140 179 old_name = settings.DATABASE_NAME 141 180 from django.db import connection 142 181 connection.creation.create_test_db(verbosity, autoclobber=not interactive) -
django/test/client.py
19 19 from django.utils.encoding import smart_str 20 20 from django.utils.http import urlencode 21 21 from django.utils.itercompat import is_iterable 22 from django.db import transaction, close_connection 22 23 23 24 BOUNDARY = 'BoUnDaRyStRiNg' 24 25 MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY … … 69 70 response = middleware_method(request, response) 70 71 response = self.apply_response_fixes(request, response) 71 72 finally: 73 signals.request_finished.disconnect(close_connection) 72 74 signals.request_finished.send(sender=self.__class__) 75 signals.request_finished.connect(close_connection) 73 76 74 77 return response 75 78 -
django/test/testcases.py
7 7 from django.core import mail 8 8 from django.core.management import call_command 9 9 from django.core.urlresolvers import clear_url_caches 10 from django.db import transaction 10 from django.db import transaction, connection 11 11 from django.http import QueryDict 12 12 from django.test import _doctest as doctest 13 13 from django.test.client import Client … … 27 27 value = [value] 28 28 return value 29 29 30 real_commit = transaction.commit 31 real_rollback = transaction.rollback 32 real_enter_transaction_management = transaction.enter_transaction_management 33 real_leave_transaction_management = transaction.leave_transaction_management 34 real_savepoint_commit = transaction.savepoint_commit 35 real_savepoint_rollback = transaction.savepoint_rollback 30 36 37 def nop(x=None): 38 return 39 40 def disable_transaction_methods(): 41 transaction.commit = nop 42 transaction.rollback = nop 43 transaction.savepoint_commit = nop 44 transaction.savepoint_rollback = nop 45 transaction.enter_transaction_management = nop 46 transaction.leave_transaction_management = nop 47 48 def restore_transaction_methods(): 49 transaction.commit = real_commit 50 transaction.rollback = real_rollback 51 transaction.savepoint_commit = real_savepoint_commit 52 transaction.savepoint_rollback = real_savepoint_rollback 53 transaction.enter_transaction_management = real_enter_transaction_management 54 transaction.leave_transaction_management = real_leave_transaction_management 55 31 56 class OutputChecker(doctest.OutputChecker): 32 57 def check_output(self, want, got, optionflags): 33 58 "The entry method for doctest output checking. Defers to a sequence of child checkers" … … 173 198 # Rollback, in case of database errors. Otherwise they'd have 174 199 # side effects on other tests. 175 200 transaction.rollback_unless_managed() 176 177 class T estCase(unittest.TestCase):201 202 class TransactionTestCase(unittest.TestCase): 178 203 def _pre_setup(self): 179 204 """Performs any pre-test setup. This includes: 180 205 … … 185 210 ROOT_URLCONF with it. 186 211 * Clearing the mail test outbox. 187 212 """ 213 self._fixture_setup() 214 self._urlconf_setup() 215 mail.outbox = [] 216 217 def _fixture_setup(self): 188 218 call_command('flush', verbosity=0, interactive=False) 189 219 if hasattr(self, 'fixtures'): 190 220 # We have to use this slightly awkward syntax due to the fact 191 221 # that we're using *args and **kwargs together. 192 222 call_command('loaddata', *self.fixtures, **{'verbosity': 0}) 223 224 def _urlconf_setup(self): 193 225 if hasattr(self, 'urls'): 194 226 self._old_root_urlconf = settings.ROOT_URLCONF 195 227 settings.ROOT_URLCONF = self.urls 196 228 clear_url_caches() 197 mail.outbox = []198 229 199 230 def __call__(self, result=None): 200 231 """ … … 211 242 import sys 212 243 result.addError(self, sys.exc_info()) 213 244 return 214 super(T estCase, self).__call__(result)245 super(TransactionTestCase, self).__call__(result) 215 246 try: 216 247 self._post_teardown() 217 248 except (KeyboardInterrupt, SystemExit): … … 226 257 227 258 * Putting back the original ROOT_URLCONF if it was changed. 228 259 """ 260 self._fixture_teardown() 261 self._urlconf_teardown() 262 263 def _fixture_teardown(self): 264 pass 265 266 def _urlconf_teardown(self): 229 267 if hasattr(self, '_old_root_urlconf'): 230 268 settings.ROOT_URLCONF = self._old_root_urlconf 231 269 clear_url_caches() … … 359 397 self.failIf(template_name in template_names, 360 398 (u"Template '%s' was used unexpectedly in rendering the" 361 399 u" response") % template_name) 400 401 class TestCase(TransactionTestCase): 402 """ 403 Does basically the same as TransactionTestCase, but surrounds every test 404 with a transaction, monkey-patches the real transaction management routines to 405 do nothing, and rollsback the test transaction at the end of the test. You have 406 to use TransactionTestCase, if you need transaction management inside a test. 407 """ 408 409 def _fixture_setup(self): 410 if not settings.DATABASE_SUPPORTS_TRANSACTIONS: 411 return super(TestCase, self)._fixture_setup() 412 413 transaction.enter_transaction_management() 414 transaction.managed(True) 415 disable_transaction_methods() 416 417 from django.contrib.sites.models import Site 418 Site.objects.clear_cache() 419 420 if hasattr(self, 'fixtures'): 421 call_command('loaddata', *self.fixtures, **{ 422 'verbosity': 0, 423 'commit': False 424 }) 425 426 def _fixture_teardown(self): 427 if not settings.DATABASE_SUPPORTS_TRANSACTIONS: 428 return super(TestCase, self)._fixture_teardown() 429 430 restore_transaction_methods() 431 transaction.rollback() 432 transaction.leave_transaction_management() 433 connection.close() 434 No newline at end of file -
django/test/__init__.py
3 3 """ 4 4 5 5 from django.test.client import Client 6 from django.test.testcases import TestCase 6 from django.test.testcases import TestCase, TransactionTestCase -
django/db/backends/creation.py
311 311 312 312 self.connection.close() 313 313 settings.DATABASE_NAME = test_database_name 314 314 settings.DATABASE_SUPPORTS_TRANSACTIONS = self._rollback_works() 315 315 316 call_command('syncdb', verbosity=verbosity, interactive=False) 316 317 317 318 if settings.CACHE_BACKEND.startswith('db://'): … … 362 363 sys.exit(1) 363 364 364 365 return test_database_name 365 366 367 def _rollback_works(self): 368 cursor = self.connection.cursor() 369 cursor.execute('CREATE TABLE ROLLBACK_TEST (X INT)') 370 self.connection._commit() 371 cursor.execute('INSERT INTO ROLLBACK_TEST (X) VALUES (8)') 372 self.connection._rollback() 373 cursor.execute('SELECT COUNT(X) FROM ROLLBACK_TEST') 374 count, = cursor.fetchone() 375 cursor.execute('DROP TABLE ROLLBACK_TEST') 376 self.connection._commit() 377 return count == 0 378 366 379 def destroy_test_db(self, old_database_name, verbosity=1): 367 380 """ 368 381 Destroy a test database, prompting the user for confirmation if the -
tests/regressiontests/file_uploads/tests.py
238 238 self.obj = FileModel() 239 239 if not os.path.isdir(temp_storage.location): 240 240 os.makedirs(temp_storage.location) 241 if os.path.isdir(UPLOAD_TO): 242 os.chmod(UPLOAD_TO, 0700) 243 shutil.rmtree(UPLOAD_TO) 241 244 242 245 def tearDown(self): 243 246 os.chmod(temp_storage.location, 0700) -
tests/regressiontests/generic_inline_admin/tests.py
21 21 # relies on content type IDs, which will vary depending on what 22 22 # other tests have been run), thus we do it here. 23 23 e = Episode.objects.create(name='This Week in Django') 24 self.episode_pk = e.pk 24 25 m = Media(content_object=e, url='http://example.com/podcast.mp3') 25 26 m.save() 27 self.media_pk = m.pk 26 28 27 29 def tearDown(self): 28 30 self.client.logout() … … 39 41 """ 40 42 A smoke test to ensure GET on the change_view works. 41 43 """ 42 response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episode/ 1/')44 response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episode/%d/' % self.episode_pk) 43 45 self.failUnlessEqual(response.status_code, 200) 44 46 45 47 def testBasicAddPost(self): … … 64 66 # inline data 65 67 "generic_inline_admin-media-content_type-object_id-TOTAL_FORMS": u"2", 66 68 "generic_inline_admin-media-content_type-object_id-INITIAL_FORMS": u"1", 67 "generic_inline_admin-media-content_type-object_id-0-id": u" 1",69 "generic_inline_admin-media-content_type-object_id-0-id": u"%d" % self.media_pk, 68 70 "generic_inline_admin-media-content_type-object_id-0-url": u"http://example.com/podcast.mp3", 69 71 "generic_inline_admin-media-content_type-object_id-1-id": u"", 70 72 "generic_inline_admin-media-content_type-object_id-1-url": u"", 71 73 } 72 response = self.client.post('/generic_inline_admin/admin/generic_inline_admin/episode/1/', post_data) 74 url = '/generic_inline_admin/admin/generic_inline_admin/episode/%d/' % self.episode_pk 75 response = self.client.post(url, post_data) 73 76 self.failUnlessEqual(response.status_code, 302) # redirect somewhere -
tests/regressiontests/comment_tests/tests/moderation_view_tests.py
8 8 9 9 def testFlagGet(self): 10 10 """GET the flag view: render a confirmation page.""" 11 self.createSomeComments() 11 comments = self.createSomeComments() 12 pk = comments[0].pk 12 13 self.client.login(username="normaluser", password="normaluser") 13 response = self.client.get("/flag/ 1/")14 response = self.client.get("/flag/%d/" % pk) 14 15 self.assertTemplateUsed(response, "comments/flag.html") 15 16 16 17 def testFlagPost(self): 17 18 """POST the flag view: actually flag the view (nice for XHR)""" 18 self.createSomeComments() 19 comments = self.createSomeComments() 20 pk = comments[0].pk 19 21 self.client.login(username="normaluser", password="normaluser") 20 response = self.client.post("/flag/ 1/")21 self.assertEqual(response["Location"], "http://testserver/flagged/?c= 1")22 c = Comment.objects.get(pk= 1)22 response = self.client.post("/flag/%d/" % pk) 23 self.assertEqual(response["Location"], "http://testserver/flagged/?c=%d" % pk) 24 c = Comment.objects.get(pk=pk) 23 25 self.assertEqual(c.flags.filter(flag=CommentFlag.SUGGEST_REMOVAL).count(), 1) 24 26 return c 25 27 26 28 def testFlagPostTwice(self): 27 29 """Users don't get to flag comments more than once.""" 28 30 c = self.testFlagPost() 29 self.client.post("/flag/ 1/")30 self.client.post("/flag/ 1/")31 self.client.post("/flag/%d/" % c.pk) 32 self.client.post("/flag/%d/" % c.pk) 31 33 self.assertEqual(c.flags.filter(flag=CommentFlag.SUGGEST_REMOVAL).count(), 1) 32 34 33 35 def testFlagAnon(self): 34 36 """GET/POST the flag view while not logged in: redirect to log in.""" 35 self.createSomeComments() 36 response = self.client.get("/flag/1/") 37 self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/flag/1/") 38 response = self.client.post("/flag/1/") 39 self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/flag/1/") 37 comments = self.createSomeComments() 38 pk = comments[0].pk 39 response = self.client.get("/flag/%d/" % pk) 40 self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/flag/%d/" % pk) 41 response = self.client.post("/flag/%d/" % pk) 42 self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/flag/%d/" % pk) 40 43 41 44 def testFlaggedView(self): 42 self.createSomeComments() 43 response = self.client.get("/flagged/", data={"c":1}) 45 comments = self.createSomeComments() 46 pk = comments[0].pk 47 response = self.client.get("/flagged/", data={"c":pk}) 44 48 self.assertTemplateUsed(response, "comments/flagged.html") 45 49 46 50 def testFlagSignals(self): … … 70 74 71 75 def testDeletePermissions(self): 72 76 """The delete view should only be accessible to 'moderators'""" 73 self.createSomeComments() 77 comments = self.createSomeComments() 78 pk = comments[0].pk 74 79 self.client.login(username="normaluser", password="normaluser") 75 response = self.client.get("/delete/ 1/")76 self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/delete/ 1/")80 response = self.client.get("/delete/%d/" % pk) 81 self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/delete/%d/" % pk) 77 82 78 83 makeModerator("normaluser") 79 response = self.client.get("/delete/ 1/")84 response = self.client.get("/delete/%d/" % pk) 80 85 self.assertEqual(response.status_code, 200) 81 86 82 87 def testDeletePost(self): 83 88 """POSTing the delete view should mark the comment as removed""" 84 self.createSomeComments() 89 comments = self.createSomeComments() 90 pk = comments[0].pk 85 91 makeModerator("normaluser") 86 92 self.client.login(username="normaluser", password="normaluser") 87 response = self.client.post("/delete/ 1/")88 self.assertEqual(response["Location"], "http://testserver/deleted/?c= 1")89 c = Comment.objects.get(pk= 1)93 response = self.client.post("/delete/%d/" % pk) 94 self.assertEqual(response["Location"], "http://testserver/deleted/?c=%d" % pk) 95 c = Comment.objects.get(pk=pk) 90 96 self.failUnless(c.is_removed) 91 97 self.assertEqual(c.flags.filter(flag=CommentFlag.MODERATOR_DELETION, user__username="normaluser").count(), 1) 92 98 … … 103 109 self.assertEqual(received_signals, [signals.comment_was_flagged]) 104 110 105 111 def testDeletedView(self): 106 self.createSomeComments() 107 response = self.client.get("/deleted/", data={"c":1}) 112 comments = self.createSomeComments() 113 pk = comments[0].pk 114 response = self.client.get("/deleted/", data={"c":pk}) 108 115 self.assertTemplateUsed(response, "comments/deleted.html") 109 116 110 117 class ApproveViewTests(CommentTestCase): 111 118 112 119 def testApprovePermissions(self): 113 120 """The delete view should only be accessible to 'moderators'""" 114 self.createSomeComments() 121 comments = self.createSomeComments() 122 pk = comments[0].pk 115 123 self.client.login(username="normaluser", password="normaluser") 116 response = self.client.get("/approve/ 1/")117 self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/approve/ 1/")124 response = self.client.get("/approve/%d/" % pk) 125 self.assertEqual(response["Location"], "http://testserver/accounts/login/?next=/approve/%d/" % pk) 118 126 119 127 makeModerator("normaluser") 120 response = self.client.get("/approve/ 1/")128 response = self.client.get("/approve/%d/" % pk) 121 129 self.assertEqual(response.status_code, 200) 122 130 123 131 def testApprovePost(self): … … 127 135 128 136 makeModerator("normaluser") 129 137 self.client.login(username="normaluser", password="normaluser") 130 response = self.client.post("/approve/ 1/")131 self.assertEqual(response["Location"], "http://testserver/approved/?c= 1")132 c = Comment.objects.get(pk= 1)138 response = self.client.post("/approve/%d/" % c1.pk) 139 self.assertEqual(response["Location"], "http://testserver/approved/?c=%d" % c1.pk) 140 c = Comment.objects.get(pk=c1.pk) 133 141 self.failUnless(c.is_public) 134 142 self.assertEqual(c.flags.filter(flag=CommentFlag.MODERATOR_APPROVAL, user__username="normaluser").count(), 1) 135 143 … … 146 154 self.assertEqual(received_signals, [signals.comment_was_flagged]) 147 155 148 156 def testApprovedView(self): 149 self.createSomeComments() 150 response = self.client.get("/approved/", data={"c":1}) 157 comments = self.createSomeComments() 158 pk = comments[0].pk 159 response = self.client.get("/approved/", data={"c":pk}) 151 160 self.assertTemplateUsed(response, "comments/approved.html") 152 161 153 162 -
tests/regressiontests/comment_tests/tests/comment_view_tests.py
1 import re 1 2 from django.conf import settings 2 3 from django.contrib.auth.models import User 3 4 from django.contrib.comments import signals … … 5 6 from regressiontests.comment_tests.models import Article 6 7 from regressiontests.comment_tests.tests import CommentTestCase 7 8 9 post_redirect_re = re.compile(r'^http://testserver/posted/\?c=(?P<pk>\d+$)') 10 8 11 class CommentViewTests(CommentTestCase): 9 12 10 13 def testPostCommentHTTPMethods(self): … … 181 184 a = Article.objects.get(pk=1) 182 185 data = self.getValidData(a) 183 186 response = self.client.post("/post/", data) 184 self.assertEqual(response["Location"], "http://testserver/posted/?c=1") 185 187 location = response["Location"] 188 match = post_redirect_re.match(location) 189 self.failUnless(match != None, "Unexpected redirect location: %s" % location) 190 186 191 data["next"] = "/somewhere/else/" 187 192 data["comment"] = "This is another comment" 188 193 response = self.client.post("/post/", data) 189 self.assertEqual(response["Location"], "http://testserver/somewhere/else/?c=2") 194 location = response["Location"] 195 match = re.search(r"^http://testserver/somewhere/else/\?c=\d+$", location) 196 self.failUnless(match != None, "Unexpected redirect location: %s" % location) 190 197 191 198 def testCommentDoneView(self): 192 199 a = Article.objects.get(pk=1) 193 200 data = self.getValidData(a) 194 201 response = self.client.post("/post/", data) 195 response = self.client.get("/posted/", {'c':1}) 202 location = response["Location"] 203 match = post_redirect_re.match(location) 204 self.failUnless(match != None, "Unexpected redirect location: %s" % location) 205 pk = int(match.group('pk')) 206 response = self.client.get(location) 196 207 self.assertTemplateUsed(response, "comments/posted.html") 197 self.assertEqual(response.context[0]["comment"], Comment.objects.get(pk= 1))208 self.assertEqual(response.context[0]["comment"], Comment.objects.get(pk=pk)) 198 209 -
docs/topics/testing.txt
785 785 will continue to be available, but it will be augmented with some useful 786 786 additions. 787 787 788 .. versionadded:: 1.1 789 790 .. class:: TransactionTestCase() 791 792 Django ``TestCase`` classes make use of database transaction facilities, if 793 available, to speed up the process of resetting the database to a known state 794 at the beginning of each test. A consequence of this, however, is that the 795 effects of transaction commit and rollback cannot be tested by a Django 796 ``TestCase`` class. If your test requires testing of such transactional 797 behavior, you should use a Django ``TransactionTestCase``. 798 799 ``TransactionTestCase`` and ``TestCase`` are identical except for the manner 800 in which the database is reset to a known state and the ability for test code 801 to test the effects of commit and rollback. A ``TranscationTestCase`` resets 802 the database before the test runs by truncating all tables and reloading 803 initial data. A ``TransactionTestCase`` may call commit and rollback and 804 observe the effects of these calls on the database. 805 806 A ``TestCase``, on the other hand, does not truncate tables and reload initial 807 data at the beginning of a test. Instead, it encloses the test code in a 808 database transaction that is rolled back at the end of the test. It also 809 prevents the code under test from issuing any commit or rollback operations 810 on the database, to ensure that the rollback at the end of the test restores 811 the database to its initial state. In order to guarantee that all ``TestCase`` 812 code starts with a clean database, the Django test runner runs all ``TestCase`` 813 tests first, before any other tests (e.g. doctests) that may alter the 814 database without restoring it to its original state. 815 816 When running on a database that does not support rollback (e.g. MySQL with the 817 MyISAM storage engine), ``TestCase`` falls back to initializing the database 818 by truncating tables and reloading initial data. 819 820 821 .. note:: 822 The ``TestCase`` use of rollback to un-do the effects of the test code 823 may reveal previously-undetected errors in test code. For example, 824 test code that assumes primary keys values will be assigned starting at 825 one may find that assumption no longer holds true when rollbacks instead 826 of table truncation are being used to reset the database. Similarly, 827 the reordering of tests so that all ``TestCase`` classes run first may 828 reveal unexpected dependencies on test case ordering. In such cases a 829 quick fix is to switch the ``TestCase`` to a ``TransactionTestCase``. 830 A better long-term fix, that allows the test to take advantage of the 831 speed benefit of ``TestCase``, is to fix the underlying test problem. 832 833 788 834 Default test client 789 835 ~~~~~~~~~~~~~~~~~~~ 790 836