[py-svn] pytest commit a864bc3f8b16: refine unittest support to also work with twisted trial test cases better by

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Tue Nov 23 15:45:33 CET 2010


# HG changeset patch -- Bitbucket.org
# Project pytest
# URL http://bitbucket.org/hpk42/pytest/overview
# User holger krekel <holger at merlinux.eu>
# Date 1290523343 -3600
# Node ID a864bc3f8b16d5d0895d5b093e202aed4652edfd
# Parent  f34cf98d4fc9e2f326b0e946c4adf9c214ba569e
refine unittest support to also work with twisted trial test cases better by
introducing a slightly hackish way to report a failure upstream

--- a/_pytest/unittest.py
+++ b/_pytest/unittest.py
@@ -33,16 +33,40 @@ class UnitTestCase(pytest.Class):
             meth()
 
 class TestCaseFunction(pytest.Function):
+    _excinfo = None
     def setup(self):
         pass
     def teardown(self):
         pass
     def startTest(self, testcase):
         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):
-        py.builtin._reraise(*rawexcinfo)
+        self._addexcinfo(rawexcinfo)
     def addFailure(self, testcase, rawexcinfo):
-        py.builtin._reraise(*rawexcinfo)
+        self._addexcinfo(rawexcinfo)
     def addSuccess(self, testcase):
         pass
     def stopTest(self, testcase):
@@ -50,3 +74,11 @@ class TestCaseFunction(pytest.Function):
     def runtest(self):
         testcase = self.parent.obj(self.name)
         testcase(result=self)
+
+ at 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

--- a/_pytest/runner.py
+++ b/_pytest/runner.py
@@ -142,6 +142,7 @@ class BaseReport(object):
 def pytest_runtest_makereport(item, call):
     when = call.when
     keywords = dict([(x,1) for x in item.keywords])
+    excinfo = call.excinfo
     if not call.excinfo:
         outcome = "passed"
         longrepr = None
@@ -312,8 +313,9 @@ class OutcomeException(Exception):
     """ OutcomeException and its subclass instances indicate and
         contain info about test and collection outcomes.
     """
-    def __init__(self, msg=None):
+    def __init__(self, msg=None, pytrace=True):
         self.msg = msg
+        self.pytrace = pytrace
 
     def __repr__(self):
         if self.msg:
@@ -355,10 +357,10 @@ def skip(msg=""):
     raise Skipped(msg=msg)
 skip.Exception = Skipped
 
-def fail(msg=""):
+def fail(msg="", pytrace=True):
     """ explicitely fail an currently-executing test with the given Message. """
     __tracebackhide__ = True
-    raise Failed(msg=msg)
+    raise Failed(msg=msg, pytrace=pytrace)
 fail.Exception = Failed
 
 

--- a/testing/test_unittest.py
+++ b/testing/test_unittest.py
@@ -103,3 +103,65 @@ def test_class_setup(testdir):
     """)
     reprec = testdir.inline_run(testpath)
     reprec.assertoutcome(passed=3)
+
+
+ at 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()
+
+ at 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())

--- a/testing/test_runner.py
+++ b/testing/test_runner.py
@@ -334,6 +334,17 @@ def test_pytest_fail():
         s = excinfo.exconly(tryshort=True)
         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():
     try:
         pytest.skip("hello")

--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -329,6 +329,9 @@ class FunctionMixin(PyobjMixin):
 
     def repr_failure(self, excinfo, outerr=None):
         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,
             style=self.config.option.tbstyle)



More information about the pytest-commit mailing list