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 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)
|
|
||||||
|
|
Loading…
Reference in New Issue