unittest: do not use TestCase.debug() with `--pdb`
Fixes https://github.com/pytest-dev/pytest/issues/5991 Fixes https://github.com/pytest-dev/pytest/issues/3823 Ref: https://github.com/pytest-dev/pytest-django/issues/772 Ref: https://github.com/pytest-dev/pytest/pull/1890 Ref: https://github.com/pytest-dev/pytest-django/pull/782 - inject wrapped testMethod - adjust test_trial_error - add test for `--trace` with unittests
This commit is contained in:
parent
710e3c40e0
commit
04f27d4eb4
|
@ -0,0 +1 @@
|
|||
``--trace`` now works with unittests.
|
|
@ -0,0 +1 @@
|
|||
Fix interaction with ``--pdb`` and unittests: do not use unittest's ``TestCase.debug()``.
|
|
@ -238,17 +238,6 @@ was executed ahead of the ``test_method``.
|
|||
|
||||
.. _pdb-unittest-note:
|
||||
|
||||
.. note::
|
||||
|
||||
Running tests from ``unittest.TestCase`` subclasses with ``--pdb`` will
|
||||
disable tearDown and cleanup methods for the case that an Exception
|
||||
occurs. This allows proper post mortem debugging for all applications
|
||||
which have significant logic in their tearDown machinery. However,
|
||||
supporting this feature has the following side effect: If people
|
||||
overwrite ``unittest.TestCase`` ``__call__`` or ``run``, they need to
|
||||
to overwrite ``debug`` in the same way (this is also true for standard
|
||||
unittest).
|
||||
|
||||
.. note::
|
||||
|
||||
Due to architectural differences between the two frameworks, setup and
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
""" discovery and running of std-library "unittest" style tests. """
|
||||
import functools
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
|
@ -107,6 +108,7 @@ class TestCaseFunction(Function):
|
|||
nofuncargs = True
|
||||
_excinfo = None
|
||||
_testcase = None
|
||||
_need_tearDown = None
|
||||
|
||||
def setup(self):
|
||||
self._testcase = self.parent.obj(self.name)
|
||||
|
@ -115,6 +117,8 @@ class TestCaseFunction(Function):
|
|||
self._request._fillfixtures()
|
||||
|
||||
def teardown(self):
|
||||
if self._need_tearDown:
|
||||
self._testcase.tearDown()
|
||||
self._testcase = None
|
||||
self._obj = None
|
||||
|
||||
|
@ -187,29 +191,45 @@ class TestCaseFunction(Function):
|
|||
def stopTest(self, testcase):
|
||||
pass
|
||||
|
||||
def _handle_skip(self):
|
||||
# implements the skipping machinery (see #2137)
|
||||
# analog to pythons Lib/unittest/case.py:run
|
||||
testMethod = getattr(self._testcase, self._testcase._testMethodName)
|
||||
if getattr(self._testcase.__class__, "__unittest_skip__", False) or getattr(
|
||||
testMethod, "__unittest_skip__", False
|
||||
):
|
||||
# If the class or method was skipped.
|
||||
skip_why = getattr(
|
||||
self._testcase.__class__, "__unittest_skip_why__", ""
|
||||
) or getattr(testMethod, "__unittest_skip_why__", "")
|
||||
self._testcase._addSkip(self, self._testcase, skip_why)
|
||||
return True
|
||||
return False
|
||||
|
||||
def runtest(self):
|
||||
if self.config.pluginmanager.get_plugin("pdbinvoke") is None:
|
||||
testMethod = getattr(self._testcase, self._testcase._testMethodName)
|
||||
|
||||
class _GetOutOf_testPartExecutor(KeyboardInterrupt):
|
||||
"""Helper exception to get out of unittests's testPartExecutor."""
|
||||
|
||||
unittest = sys.modules.get("unittest")
|
||||
|
||||
reraise = ()
|
||||
if unittest:
|
||||
reraise += (unittest.SkipTest,)
|
||||
|
||||
@functools.wraps(testMethod)
|
||||
def wrapped_testMethod(*args, **kwargs):
|
||||
try:
|
||||
self.ihook.pytest_pyfunc_call(pyfuncitem=self)
|
||||
except reraise:
|
||||
raise
|
||||
except Exception as exc:
|
||||
expecting_failure_method = getattr(
|
||||
testMethod, "__unittest_expecting_failure__", False
|
||||
)
|
||||
expecting_failure_class = getattr(
|
||||
self, "__unittest_expecting_failure__", False
|
||||
)
|
||||
expecting_failure = expecting_failure_class or expecting_failure_method
|
||||
self._need_tearDown = True
|
||||
|
||||
if expecting_failure:
|
||||
raise
|
||||
|
||||
raise _GetOutOf_testPartExecutor(exc)
|
||||
|
||||
self._testcase._wrapped_testMethod = wrapped_testMethod
|
||||
self._testcase._testMethodName = "_wrapped_testMethod"
|
||||
try:
|
||||
self._testcase(result=self)
|
||||
else:
|
||||
# disables tearDown and cleanups for post mortem debugging (see #1890)
|
||||
if self._handle_skip():
|
||||
return
|
||||
self._testcase.debug()
|
||||
except _GetOutOf_testPartExecutor as exc:
|
||||
raise exc.args[0] from exc.args[0]
|
||||
|
||||
def _prunetraceback(self, excinfo):
|
||||
Function._prunetraceback(self, excinfo)
|
||||
|
|
|
@ -537,24 +537,28 @@ class TestTrialUnittest:
|
|||
)
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"test_trial_error.py::TC::test_four FAILED",
|
||||
"test_trial_error.py::TC::test_four SKIPPED",
|
||||
"test_trial_error.py::TC::test_four ERROR",
|
||||
"test_trial_error.py::TC::test_one FAILED",
|
||||
"test_trial_error.py::TC::test_three FAILED",
|
||||
"test_trial_error.py::TC::test_two FAILED",
|
||||
"test_trial_error.py::TC::test_two SKIPPED",
|
||||
"test_trial_error.py::TC::test_two ERROR",
|
||||
"*ERRORS*",
|
||||
"*_ ERROR at teardown of TC.test_four _*",
|
||||
"NOTE: Incompatible Exception Representation, displaying natively:",
|
||||
"*DelayedCalls*",
|
||||
"*_ ERROR at teardown of TC.test_two _*",
|
||||
"NOTE: Incompatible Exception Representation, displaying natively:",
|
||||
"*DelayedCalls*",
|
||||
"*= FAILURES =*",
|
||||
"*_ TC.test_four _*",
|
||||
"*NameError*crash*",
|
||||
# "*_ TC.test_four _*",
|
||||
# "*NameError*crash*",
|
||||
"*_ TC.test_one _*",
|
||||
"*NameError*crash*",
|
||||
"*_ TC.test_three _*",
|
||||
"NOTE: Incompatible Exception Representation, displaying natively:",
|
||||
"*DelayedCalls*",
|
||||
"*_ TC.test_two _*",
|
||||
"*NameError*crash*",
|
||||
"*= 4 failed, 1 error in *",
|
||||
"*= 2 failed, 2 skipped, 2 errors in *",
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -1096,3 +1100,32 @@ def test_exit_outcome(testdir):
|
|||
)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(["*Exit: pytest_exit called*", "*= no tests ran in *"])
|
||||
|
||||
|
||||
def test_trace(testdir, monkeypatch):
|
||||
calls = []
|
||||
|
||||
def check_call(*args, **kwargs):
|
||||
calls.append((args, kwargs))
|
||||
assert args == ("runcall",)
|
||||
|
||||
class _pdb:
|
||||
def runcall(*args, **kwargs):
|
||||
calls.append((args, kwargs))
|
||||
|
||||
return _pdb
|
||||
|
||||
monkeypatch.setattr("_pytest.debugging.pytestPDB._init_pdb", check_call)
|
||||
|
||||
p1 = testdir.makepyfile(
|
||||
"""
|
||||
import unittest
|
||||
|
||||
class MyTestCase(unittest.TestCase):
|
||||
def test(self):
|
||||
self.assertEqual('foo', 'foo')
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("--trace", str(p1))
|
||||
assert len(calls) == 2
|
||||
assert result.ret == 0
|
||||
|
|
Loading…
Reference in New Issue