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:
holger krekel 2010-11-23 15:42:23 +01:00
parent 6e6b0ab5d9
commit 695bffc83d
5 changed files with 115 additions and 5 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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())