Ticket #20392: refactor_testcase_transactions.diff
File refactor_testcase_transactions.diff, 7.8 KB (added by , 12 years ago) |
---|
-
django/test/testcases.py
commit 967d5501a6f0b166e78c471c80aabc117e12a914 Author: Raphaël Barrois <raphael.barrois@polytechnique.org> Date: Sat May 11 00:26:34 2013 +0200 test.TestCase: Use a single transaction per class. And wrap each test_XXX in a atomic() call. diff --git a/django/test/testcases.py b/django/test/testcases.py index a9fcc2b..d4bde8d 100644
a b class TransactionTestCase(SimpleTestCase): 448 448 # test case 449 449 reset_sequences = False 450 450 451 # Subclasses can ask for connection closure after each test or each TestCase 452 reset_connections_per_test = True 453 451 454 def _pre_setup(self): 452 455 """Performs any pre-test setup. This includes: 453 456 … … class TransactionTestCase(SimpleTestCase): 463 466 self._urlconf_setup() 464 467 mail.outbox = [] 465 468 466 def _databases_names(self, include_mirrors=True): 469 @classmethod 470 def _databases_names(cls, include_mirrors=True): 467 471 # If the test case has a multi_db=True flag, act on all databases, 468 472 # including mirrors or not. Otherwise, just on the default DB. 469 if getattr( self, 'multi_db', False):473 if getattr(cls, 'multi_db', False): 470 474 return [alias for alias in connections 471 475 if include_mirrors or not connections[alias].settings_dict['TEST_MIRROR']] 472 476 else: … … class TransactionTestCase(SimpleTestCase): 519 523 # be created with the wrong time). 520 524 # To make sure this doesn't happen, get a clean connection at the 521 525 # start of every test. 526 if self.reset_connections_per_test: 527 for conn in connections.all(): 528 conn.close() 529 530 @classmethod 531 def tearDownClass(cls): 532 super(TransactionTestCase, cls).tearDownClass() 533 534 # Ensure connections are closed, no matter what. 535 # django.db.backends.BaseDatabaseWrapper.close() behaves properly 536 # on already closed connections. 522 537 for conn in connections.all(): 523 538 conn.close() 524 539 … … class TestCase(TransactionTestCase): 818 833 inside a test. 819 834 """ 820 835 836 # For savepoints to be useful, connections should only be closed at 837 # tearDownClass(). 838 reset_connections_per_test = False 839 821 840 def _fixture_setup(self): 822 841 if not connections_support_transactions(): 823 842 return super(TestCase, self)._fixture_setup() … … class TestCase(TransactionTestCase): 828 847 for db_name in self._databases_names(): 829 848 self.atomics[db_name] = transaction.atomic(using=db_name) 830 849 self.atomics[db_name].__enter__() 831 # Remove this when the legacy transaction management goes away.832 disable_transaction_methods()833 850 834 851 for db in self._databases_names(include_mirrors=False): 835 852 if hasattr(self, 'fixtures'): … … class TestCase(TransactionTestCase): 845 862 if not connections_support_transactions(): 846 863 return super(TestCase, self)._fixture_teardown() 847 864 865 for db_name in reversed(self._databases_names()): 866 if connections[db_name].connection is None: 867 # Already closed 868 continue 869 870 # Hack to force a rollback: populate the exception fields 871 self.atomics[db_name].__exit__(ValueError, "Rollback", None) 872 873 @classmethod 874 def setUpClass(cls): 875 if not connections_support_transactions(): 876 return super(TestCase, cls).setUpClass() 877 878 cls.class_atomics = {} 879 for db_name in cls._databases_names(include_mirrors=False): 880 cls.class_atomics[db_name] = transaction.atomic(using=db_name) 881 cls.class_atomics[db_name].__enter__() 882 883 # Remove this when the legacy transaction management goes away. 884 disable_transaction_methods() 885 886 return super(TestCase, cls).setUpClass() 887 888 @classmethod 889 def tearDownClass(cls): 890 if not connections_support_transactions(): 891 return super(TestCase, cls).tearDownClass() 892 848 893 # Remove this when the legacy transaction management goes away. 849 894 restore_transaction_methods() 850 for db_name in reversed(self._databases_names()): 851 # Hack to force a rollback 852 connections[db_name].needs_rollback = True 853 self.atomics[db_name].__exit__(None, None, None) 895 896 for db_name in reversed(cls._databases_names()): 897 if connections[db_name].connection is None: 898 # Already closed 899 continue 900 901 cls.class_atomics[db_name].__exit__(ValueError, "Rollback", None) 902 903 return super(TestCase, cls).tearDownClass() 854 904 855 905 856 906 def _deferredSkip(condition, reason): -
docs/topics/testing/overview.txt
diff --git a/docs/topics/testing/overview.txt b/docs/topics/testing/overview.txt index 9228a07..272c976 100644
a b additions, including: 987 987 988 988 * Automatic loading of fixtures. 989 989 990 * Wraps each test in a transaction. 990 * Wraps each TestCase in a single transaction, and each test in a 991 SAVEPOINT/ROLLBACK pair; this allows creating objects within 992 :meth:`~django.test.TestCase.setUpClass` instead of 993 :meth:`~django.test.TestCase.setUp`. 991 994 992 995 * Creates a TestClient instance. 993 996 … … additions, including: 999 1002 The order in which tests are run has changed. See `Order in which tests are 1000 1003 executed`_. 1001 1004 1005 .. versionchanged:: 1.6 1006 1007 Prior to 1.6, each test ran inside a dedicated transaction. 1008 A transaction is now opened at :meth:`~django.test.TestCase.setUpClass`, 1009 and each test runs within a savepoint. 1010 1011 This allows model creation in :meth:`~django.test.TestCase.setUpClass`, 1012 that will stay unaltered by each test's code. 1013 1002 1014 ``TestCase`` inherits from :class:`~django.test.TransactionTestCase`. 1003 1015 1004 1016 .. _live-test-server: -
tests/test_utils/tests.py
diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index d72a482..a8a7665 100644
a b class SkippingExtraTests(TestCase): 598 598 pass 599 599 600 600 601 class AtomicSetUpClass1(TestCase): 602 person_pk = 0 603 604 @classmethod 605 def setUpClass(cls): 606 super(AtomicSetUpClass1, cls).setUpClass() 607 person = Person.objects.create(name='setUpClass1') 608 if cls.person_pk == 0: 609 cls.person_pk = person.pk 610 611 def test_setupclass_called(self): 612 p = Person.objects.get(name='setUpClass1') 613 self.assertEqual(self.person_pk, p.pk) 614 615 def test_setupclass_called_only_once(self): 616 """Ensure the Person is created only once.""" 617 p = Person.objects.get(name='setUpClass1') 618 self.assertEqual(self.person_pk, p.pk) 619 620 def test_other_setupclass_rolled_back(self): 621 """ 622 Ensure the Person created by AtomicSetUpClass2 doesn't exist. 623 """ 624 persons = list(Person.objects.all()) 625 self.assertEqual(1, len(persons)) 626 person = persons[0] 627 self.assertEqual(self.person_pk, person.pk) 628 self.assertEqual('setUpClass1', person.name) 629 630 631 class AtomicSetUpClass2(TestCase): 632 """ 633 Second TestCase, to ensure that setUpClass is rolled back between TestCase. 634 """ 635 person_pk = 0 636 @classmethod 637 def setUpClass(cls): 638 super(AtomicSetUpClass2, cls).setUpClass() 639 person = Person.objects.create(name='setUpClass2') 640 if cls.person_pk == 0: 641 cls.person_pk = person.pk 642 643 def test_other_setupclass_rolled_back(self): 644 """ 645 Ensure the Person created by AtomicSetUpClass1 doesn't exist anymore. 646 """ 647 persons = list(Person.objects.all()) 648 self.assertEqual(1, len(persons)) 649 person = persons[0] 650 self.assertEqual(self.person_pk, person.pk) 651 self.assertEqual('setUpClass2', person.name) 652 653 601 654 class AssertRaisesMsgTest(SimpleTestCase): 602 655 603 656 def test_special_re_chars(self):