mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #27391 -- Implemented SimpleTestCase.debug().
debug() should bubbled up exceptions if occurring in test, but behave the same as run() when no exceptions occurred.
This commit is contained in:
		
				
					committed by
					
						 Mariusz Felisiak
						Mariusz Felisiak
					
				
			
			
				
	
			
			
			
						parent
						
							dc8cd2fefd
						
					
				
				
					commit
					1711c509fa
				
			| @@ -9,6 +9,7 @@ from contextlib import contextmanager | |||||||
| from copy import copy | from copy import copy | ||||||
| from difflib import get_close_matches | from difflib import get_close_matches | ||||||
| from functools import wraps | from functools import wraps | ||||||
|  | from unittest.suite import _DebugResult | ||||||
| from unittest.util import safe_repr | from unittest.util import safe_repr | ||||||
| from urllib.parse import ( | from urllib.parse import ( | ||||||
|     parse_qsl, unquote, urlencode, urljoin, urlparse, urlsplit, urlunparse, |     parse_qsl, unquote, urlencode, urljoin, urlparse, urlsplit, urlunparse, | ||||||
| @@ -235,6 +236,21 @@ class SimpleTestCase(unittest.TestCase): | |||||||
|         set up. This means that user-defined Test Cases aren't required to |         set up. This means that user-defined Test Cases aren't required to | ||||||
|         include a call to super().setUp(). |         include a call to super().setUp(). | ||||||
|         """ |         """ | ||||||
|  |         self._setup_and_call(result) | ||||||
|  |  | ||||||
|  |     def debug(self): | ||||||
|  |         """Perform the same as __call__(), without catching the exception.""" | ||||||
|  |         debug_result = _DebugResult() | ||||||
|  |         self._setup_and_call(debug_result, debug=True) | ||||||
|  |  | ||||||
|  |     def _setup_and_call(self, result, debug=False): | ||||||
|  |         """ | ||||||
|  |         Perform the following in order: pre-setup, run test, post-teardown, | ||||||
|  |         skipping pre/post hooks if test is set to be skipped. | ||||||
|  |  | ||||||
|  |         If debug=True, reraise any errors in setup and use super().debug() | ||||||
|  |         instead of __call__() to run the test. | ||||||
|  |         """ | ||||||
|         testMethod = getattr(self, self._testMethodName) |         testMethod = getattr(self, self._testMethodName) | ||||||
|         skipped = ( |         skipped = ( | ||||||
|             getattr(self.__class__, "__unittest_skip__", False) or |             getattr(self.__class__, "__unittest_skip__", False) or | ||||||
| @@ -245,13 +261,20 @@ class SimpleTestCase(unittest.TestCase): | |||||||
|             try: |             try: | ||||||
|                 self._pre_setup() |                 self._pre_setup() | ||||||
|             except Exception: |             except Exception: | ||||||
|  |                 if debug: | ||||||
|  |                     raise | ||||||
|                 result.addError(self, sys.exc_info()) |                 result.addError(self, sys.exc_info()) | ||||||
|                 return |                 return | ||||||
|         super().__call__(result) |         if debug: | ||||||
|  |             super().debug() | ||||||
|  |         else: | ||||||
|  |             super().__call__(result) | ||||||
|         if not skipped: |         if not skipped: | ||||||
|             try: |             try: | ||||||
|                 self._post_teardown() |                 self._post_teardown() | ||||||
|             except Exception: |             except Exception: | ||||||
|  |                 if debug: | ||||||
|  |                     raise | ||||||
|                 result.addError(self, sys.exc_info()) |                 result.addError(self, sys.exc_info()) | ||||||
|                 return |                 return | ||||||
|  |  | ||||||
|   | |||||||
| @@ -198,7 +198,9 @@ Templates | |||||||
| Tests | Tests | ||||||
| ~~~~~ | ~~~~~ | ||||||
|  |  | ||||||
| * ... | * :class:`~django.test.SimpleTestCase` now implements the ``debug()`` method to | ||||||
|  |   allow running a test without collecting the result and catching exceptions. | ||||||
|  |   This can be used to support running tests under a debugger. | ||||||
|  |  | ||||||
| URLs | URLs | ||||||
| ~~~~ | ~~~~ | ||||||
|   | |||||||
| @@ -775,6 +775,11 @@ If your tests make any database queries, use subclasses | |||||||
|     :exc:`unittest.SkipTest` in ``setUpClass()``, be sure to do it before |     :exc:`unittest.SkipTest` in ``setUpClass()``, be sure to do it before | ||||||
|     calling ``super()`` to avoid this. |     calling ``super()`` to avoid this. | ||||||
|  |  | ||||||
|  | .. versionchanged:: 3.1 | ||||||
|  |  | ||||||
|  |     The ``debug()`` method was implemented to allow running a test without | ||||||
|  |     collecting the result and catching exceptions. | ||||||
|  |  | ||||||
| ``TransactionTestCase`` | ``TransactionTestCase`` | ||||||
| ----------------------- | ----------------------- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -25,6 +25,11 @@ class DebugInvocationTests(SimpleTestCase): | |||||||
|     def get_runner(self): |     def get_runner(self): | ||||||
|         return unittest.TextTestRunner(stream=StringIO()) |         return unittest.TextTestRunner(stream=StringIO()) | ||||||
|  |  | ||||||
|  |     def isolate_debug_test(self, test_suite, result): | ||||||
|  |         # Suite teardown needs to be manually called to isolate failures. | ||||||
|  |         test_suite._tearDownPreviousClass(None, result) | ||||||
|  |         test_suite._handleModuleTearDown(result) | ||||||
|  |  | ||||||
|     def test_run_cleanup(self, _pre_setup, _post_teardown): |     def test_run_cleanup(self, _pre_setup, _post_teardown): | ||||||
|         """Simple test run: catches errors and runs cleanup.""" |         """Simple test run: catches errors and runs cleanup.""" | ||||||
|         test_suite = unittest.TestSuite() |         test_suite = unittest.TestSuite() | ||||||
| @@ -76,6 +81,58 @@ class DebugInvocationTests(SimpleTestCase): | |||||||
|         self.assertFalse(_post_teardown.called) |         self.assertFalse(_post_teardown.called) | ||||||
|         self.assertFalse(_pre_setup.called) |         self.assertFalse(_pre_setup.called) | ||||||
|  |  | ||||||
|  |     def test_debug_cleanup(self, _pre_setup, _post_teardown): | ||||||
|  |         """Simple debug run without errors.""" | ||||||
|  |         test_suite = unittest.TestSuite() | ||||||
|  |         test_suite.addTest(ErrorTestCase('simple_test')) | ||||||
|  |         test_suite.debug() | ||||||
|  |         _pre_setup.assert_called_once_with() | ||||||
|  |         _post_teardown.assert_called_once_with() | ||||||
|  |  | ||||||
|  |     def test_debug_bubbles_error(self, _pre_setup, _post_teardown): | ||||||
|  |         """debug() bubbles up exceptions before cleanup.""" | ||||||
|  |         test_suite = unittest.TestSuite() | ||||||
|  |         test_suite.addTest(ErrorTestCase('raising_test')) | ||||||
|  |         msg = 'debug() bubbles up exceptions before cleanup.' | ||||||
|  |         with self.assertRaisesMessage(Exception, msg): | ||||||
|  |             # This is the same as test_suite.debug(). | ||||||
|  |             result = _DebugResult() | ||||||
|  |             test_suite.run(result, debug=True) | ||||||
|  |         # pre-setup is called but not post-teardown. | ||||||
|  |         _pre_setup.assert_called_once_with() | ||||||
|  |         self.assertFalse(_post_teardown.called) | ||||||
|  |         self.isolate_debug_test(test_suite, result) | ||||||
|  |  | ||||||
|  |     def test_debug_bubbles_pre_setup_error(self, _pre_setup, _post_teardown): | ||||||
|  |         """debug() bubbles up exceptions during _pre_setup.""" | ||||||
|  |         msg = 'Exception in _pre_setup.' | ||||||
|  |         _pre_setup.side_effect = Exception(msg) | ||||||
|  |         test_suite = unittest.TestSuite() | ||||||
|  |         test_suite.addTest(ErrorTestCase('simple_test')) | ||||||
|  |         with self.assertRaisesMessage(Exception, msg): | ||||||
|  |             # This is the same as test_suite.debug(). | ||||||
|  |             result = _DebugResult() | ||||||
|  |             test_suite.run(result, debug=True) | ||||||
|  |         # pre-setup is called but not post-teardown. | ||||||
|  |         _pre_setup.assert_called_once_with() | ||||||
|  |         self.assertFalse(_post_teardown.called) | ||||||
|  |         self.isolate_debug_test(test_suite, result) | ||||||
|  |  | ||||||
|  |     def test_debug_bubbles_post_teardown_error(self, _pre_setup, _post_teardown): | ||||||
|  |         """debug() bubbles up exceptions during _post_teardown.""" | ||||||
|  |         msg = 'Exception in _post_teardown.' | ||||||
|  |         _post_teardown.side_effect = Exception(msg) | ||||||
|  |         test_suite = unittest.TestSuite() | ||||||
|  |         test_suite.addTest(ErrorTestCase('simple_test')) | ||||||
|  |         with self.assertRaisesMessage(Exception, msg): | ||||||
|  |             # This is the same as test_suite.debug(). | ||||||
|  |             result = _DebugResult() | ||||||
|  |             test_suite.run(result, debug=True) | ||||||
|  |         # pre-setup and post-teardwn are called. | ||||||
|  |         _pre_setup.assert_called_once_with() | ||||||
|  |         _post_teardown.assert_called_once_with() | ||||||
|  |         self.isolate_debug_test(test_suite, result) | ||||||
|  |  | ||||||
|     def test_debug_skipped_test_no_cleanup(self, _pre_setup, _post_teardown): |     def test_debug_skipped_test_no_cleanup(self, _pre_setup, _post_teardown): | ||||||
|         test_suite = unittest.TestSuite() |         test_suite = unittest.TestSuite() | ||||||
|         test_suite.addTest(ErrorTestCase('skipped_test')) |         test_suite.addTest(ErrorTestCase('skipped_test')) | ||||||
| @@ -85,6 +142,4 @@ class DebugInvocationTests(SimpleTestCase): | |||||||
|             test_suite.run(result, debug=True) |             test_suite.run(result, debug=True) | ||||||
|         self.assertFalse(_post_teardown.called) |         self.assertFalse(_post_teardown.called) | ||||||
|         self.assertFalse(_pre_setup.called) |         self.assertFalse(_pre_setup.called) | ||||||
|         # Suite teardown needs to be manually called to isolate failure. |         self.isolate_debug_test(test_suite, result) | ||||||
|         test_suite._tearDownPreviousClass(None, result) |  | ||||||
|         test_suite._handleModuleTearDown(result) |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user