refine unittest support to also work with twisted trial test cases better by
introducing a slightly hackish way to report a failure upstream
This commit is contained in:
parent
6e6b0ab5d9
commit
695bffc83d
|
@ -329,6 +329,9 @@ class FunctionMixin(PyobjMixin):
|
||||||
|
|
||||||
def repr_failure(self, excinfo, outerr=None):
|
def repr_failure(self, excinfo, outerr=None):
|
||||||
assert outerr is None, "XXX outerr usage is deprecated"
|
assert outerr is None, "XXX outerr usage is deprecated"
|
||||||
|
if excinfo.errisinstance(pytest.fail.Exception):
|
||||||
|
if not excinfo.value.pytrace:
|
||||||
|
return str(excinfo.value)
|
||||||
return self._repr_failure_py(excinfo,
|
return self._repr_failure_py(excinfo,
|
||||||
style=self.config.option.tbstyle)
|
style=self.config.option.tbstyle)
|
||||||
|
|
||||||
|
|
|
@ -142,6 +142,7 @@ class BaseReport(object):
|
||||||
def pytest_runtest_makereport(item, call):
|
def pytest_runtest_makereport(item, call):
|
||||||
when = call.when
|
when = call.when
|
||||||
keywords = dict([(x,1) for x in item.keywords])
|
keywords = dict([(x,1) for x in item.keywords])
|
||||||
|
excinfo = call.excinfo
|
||||||
if not call.excinfo:
|
if not call.excinfo:
|
||||||
outcome = "passed"
|
outcome = "passed"
|
||||||
longrepr = None
|
longrepr = None
|
||||||
|
@ -312,8 +313,9 @@ class OutcomeException(Exception):
|
||||||
""" OutcomeException and its subclass instances indicate and
|
""" OutcomeException and its subclass instances indicate and
|
||||||
contain info about test and collection outcomes.
|
contain info about test and collection outcomes.
|
||||||
"""
|
"""
|
||||||
def __init__(self, msg=None):
|
def __init__(self, msg=None, pytrace=True):
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
self.pytrace = pytrace
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self.msg:
|
if self.msg:
|
||||||
|
@ -355,10 +357,10 @@ def skip(msg=""):
|
||||||
raise Skipped(msg=msg)
|
raise Skipped(msg=msg)
|
||||||
skip.Exception = Skipped
|
skip.Exception = Skipped
|
||||||
|
|
||||||
def fail(msg=""):
|
def fail(msg="", pytrace=True):
|
||||||
""" explicitely fail an currently-executing test with the given Message. """
|
""" explicitely fail an currently-executing test with the given Message. """
|
||||||
__tracebackhide__ = True
|
__tracebackhide__ = True
|
||||||
raise Failed(msg=msg)
|
raise Failed(msg=msg, pytrace=pytrace)
|
||||||
fail.Exception = Failed
|
fail.Exception = Failed
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,16 +33,40 @@ class UnitTestCase(pytest.Class):
|
||||||
meth()
|
meth()
|
||||||
|
|
||||||
class TestCaseFunction(pytest.Function):
|
class TestCaseFunction(pytest.Function):
|
||||||
|
_excinfo = None
|
||||||
def setup(self):
|
def setup(self):
|
||||||
pass
|
pass
|
||||||
def teardown(self):
|
def teardown(self):
|
||||||
pass
|
pass
|
||||||
def startTest(self, testcase):
|
def startTest(self, testcase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _addexcinfo(self, rawexcinfo):
|
||||||
|
#__tracebackhide__ = True
|
||||||
|
assert rawexcinfo
|
||||||
|
try:
|
||||||
|
self._excinfo = py.code.ExceptionInfo(rawexcinfo)
|
||||||
|
except TypeError:
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
l = py.std.traceback.format_exception(*rawexcinfo)
|
||||||
|
l.insert(0, "NOTE: Incompatible Exception Representation, "
|
||||||
|
"displaying natively:\n\n")
|
||||||
|
pytest.fail("".join(l), pytrace=False)
|
||||||
|
except (pytest.fail.Exception, KeyboardInterrupt):
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
pytest.fail("ERROR: Unknown Incompatible Exception "
|
||||||
|
"representation:\n%r" %(rawexcinfo,), pytrace=False)
|
||||||
|
except pytest.fail.Exception:
|
||||||
|
self._excinfo = py.code.ExceptionInfo()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise
|
||||||
|
|
||||||
def addError(self, testcase, rawexcinfo):
|
def addError(self, testcase, rawexcinfo):
|
||||||
py.builtin._reraise(*rawexcinfo)
|
self._addexcinfo(rawexcinfo)
|
||||||
def addFailure(self, testcase, rawexcinfo):
|
def addFailure(self, testcase, rawexcinfo):
|
||||||
py.builtin._reraise(*rawexcinfo)
|
self._addexcinfo(rawexcinfo)
|
||||||
def addSuccess(self, testcase):
|
def addSuccess(self, testcase):
|
||||||
pass
|
pass
|
||||||
def stopTest(self, testcase):
|
def stopTest(self, testcase):
|
||||||
|
@ -50,3 +74,11 @@ class TestCaseFunction(pytest.Function):
|
||||||
def runtest(self):
|
def runtest(self):
|
||||||
testcase = self.parent.obj(self.name)
|
testcase = self.parent.obj(self.name)
|
||||||
testcase(result=self)
|
testcase(result=self)
|
||||||
|
|
||||||
|
@pytest.mark.tryfirst
|
||||||
|
def pytest_runtest_makereport(item, call):
|
||||||
|
if isinstance(item, TestCaseFunction):
|
||||||
|
if item._excinfo:
|
||||||
|
call.excinfo = item._excinfo
|
||||||
|
item._excinfo = None
|
||||||
|
del call.result
|
||||||
|
|
|
@ -334,6 +334,17 @@ def test_pytest_fail():
|
||||||
s = excinfo.exconly(tryshort=True)
|
s = excinfo.exconly(tryshort=True)
|
||||||
assert s.startswith("Failed")
|
assert s.startswith("Failed")
|
||||||
|
|
||||||
|
def test_pytest_fail_notrace(testdir):
|
||||||
|
testdir.makepyfile("""
|
||||||
|
import pytest
|
||||||
|
def test_hello():
|
||||||
|
pytest.fail("hello", pytrace=False)
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"hello"
|
||||||
|
])
|
||||||
|
|
||||||
def test_exception_printing_skip():
|
def test_exception_printing_skip():
|
||||||
try:
|
try:
|
||||||
pytest.skip("hello")
|
pytest.skip("hello")
|
||||||
|
|
|
@ -103,3 +103,65 @@ def test_class_setup(testdir):
|
||||||
""")
|
""")
|
||||||
reprec = testdir.inline_run(testpath)
|
reprec = testdir.inline_run(testpath)
|
||||||
reprec.assertoutcome(passed=3)
|
reprec.assertoutcome(passed=3)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.multi(type=['Error', 'Failure'])
|
||||||
|
def test_testcase_adderrorandfailure_defers(testdir, type):
|
||||||
|
testdir.makepyfile("""
|
||||||
|
from unittest import TestCase
|
||||||
|
import pytest
|
||||||
|
class MyTestCase(TestCase):
|
||||||
|
def run(self, result):
|
||||||
|
excinfo = pytest.raises(ZeroDivisionError, lambda: 0/0)
|
||||||
|
try:
|
||||||
|
result.add%s(self, excinfo._excinfo)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
pytest.fail("add%s should not raise")
|
||||||
|
def test_hello(self):
|
||||||
|
pass
|
||||||
|
""" % (type, type))
|
||||||
|
result = testdir.runpytest()
|
||||||
|
assert 'should not raise' not in result.stdout.str()
|
||||||
|
|
||||||
|
@pytest.mark.multi(type=['Error', 'Failure'])
|
||||||
|
def test_testcase_custom_exception_info(testdir, type):
|
||||||
|
testdir.makepyfile("""
|
||||||
|
from unittest import TestCase
|
||||||
|
import py, pytest
|
||||||
|
class MyTestCase(TestCase):
|
||||||
|
def run(self, result):
|
||||||
|
excinfo = pytest.raises(ZeroDivisionError, lambda: 0/0)
|
||||||
|
# we fake an incompatible exception info
|
||||||
|
from _pytest.monkeypatch import monkeypatch
|
||||||
|
mp = monkeypatch()
|
||||||
|
def t(*args):
|
||||||
|
mp.undo()
|
||||||
|
raise TypeError()
|
||||||
|
mp.setattr(py.code, 'ExceptionInfo', t)
|
||||||
|
try:
|
||||||
|
excinfo = excinfo._excinfo
|
||||||
|
result.add%(type)s(self, excinfo)
|
||||||
|
finally:
|
||||||
|
mp.undo()
|
||||||
|
def test_hello(self):
|
||||||
|
pass
|
||||||
|
""" % locals())
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"NOTE: Incompatible Exception Representation*",
|
||||||
|
"*ZeroDivisionError*",
|
||||||
|
"*1 failed*",
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_testcase_totally_incompatible_exception_info(testdir):
|
||||||
|
item, = testdir.getitems("""
|
||||||
|
from unittest import TestCase
|
||||||
|
class MyTestCase(TestCase):
|
||||||
|
def test_hello(self):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
item.addError(None, 42)
|
||||||
|
excinfo = item._excinfo
|
||||||
|
assert 'ERROR: Unknown Incompatible' in str(excinfo.getrepr())
|
||||||
|
|
Loading…
Reference in New Issue