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:
parent
dc8cd2fefd
commit
1711c509fa
|
@ -9,6 +9,7 @@ from contextlib import contextmanager
|
|||
from copy import copy
|
||||
from difflib import get_close_matches
|
||||
from functools import wraps
|
||||
from unittest.suite import _DebugResult
|
||||
from unittest.util import safe_repr
|
||||
from urllib.parse import (
|
||||
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
|
||||
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)
|
||||
skipped = (
|
||||
getattr(self.__class__, "__unittest_skip__", False) or
|
||||
|
@ -245,13 +261,20 @@ class SimpleTestCase(unittest.TestCase):
|
|||
try:
|
||||
self._pre_setup()
|
||||
except Exception:
|
||||
if debug:
|
||||
raise
|
||||
result.addError(self, sys.exc_info())
|
||||
return
|
||||
super().__call__(result)
|
||||
if debug:
|
||||
super().debug()
|
||||
else:
|
||||
super().__call__(result)
|
||||
if not skipped:
|
||||
try:
|
||||
self._post_teardown()
|
||||
except Exception:
|
||||
if debug:
|
||||
raise
|
||||
result.addError(self, sys.exc_info())
|
||||
return
|
||||
|
||||
|
|
|
@ -198,7 +198,9 @@ Templates
|
|||
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
|
||||
~~~~
|
||||
|
|
|
@ -775,6 +775,11 @@ If your tests make any database queries, use subclasses
|
|||
:exc:`unittest.SkipTest` in ``setUpClass()``, be sure to do it before
|
||||
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``
|
||||
-----------------------
|
||||
|
||||
|
|
|
@ -25,6 +25,11 @@ class DebugInvocationTests(SimpleTestCase):
|
|||
def get_runner(self):
|
||||
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):
|
||||
"""Simple test run: catches errors and runs cleanup."""
|
||||
test_suite = unittest.TestSuite()
|
||||
|
@ -76,6 +81,58 @@ class DebugInvocationTests(SimpleTestCase):
|
|||
self.assertFalse(_post_teardown.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):
|
||||
test_suite = unittest.TestSuite()
|
||||
test_suite.addTest(ErrorTestCase('skipped_test'))
|
||||
|
@ -85,6 +142,4 @@ class DebugInvocationTests(SimpleTestCase):
|
|||
test_suite.run(result, debug=True)
|
||||
self.assertFalse(_post_teardown.called)
|
||||
self.assertFalse(_pre_setup.called)
|
||||
# Suite teardown needs to be manually called to isolate failure.
|
||||
test_suite._tearDownPreviousClass(None, result)
|
||||
test_suite._handleModuleTearDown(result)
|
||||
self.isolate_debug_test(test_suite, result)
|
||||
|
|
Loading…
Reference in New Issue