Commits

Anonymous committed dfc8577

Refactor TestCase.run to catch exceptions / failures uniformly in all test parts

Comments (0)

Files changed (6)

 ========================================
 
 
-Improvements from unittest in Python 3.2
+Improvements from unittest in Python 2.7
 ========================================
 
+* `assertWarns`
+* Addition of `unittestgui`
+
 
 Differences from unittest in Python Standard Library
 ====================================================
 
 * DeprecationWarning for unsupported result objects (missing addSkip method)
   became RuntimeWarning.
+* Addition of `TestCase.assertWarns` as a context manager. (Needs Python 2.5 to be used.)

unittest2/case.py

     The test was supposed to fail, but it didn't!
     """
 
+class _Outcome(object):
+    def __init__(self):
+        self.success = True
+        self.skipped = None
+        self.unexpectedSuccess = None
+        self.expectedFailure = None
+        self.errors = []
+        self.failures = []
+
 def _id(obj):
     return obj
 
            not have a method with the specified name.
         """
         self._testMethodName = methodName
-        self._resultForDoCleanups = None
+        self._outcomeForDoCleanups = None
         try:
             testMethod = getattr(self, methodName)
         except AttributeError:
         if addSkip is not None:
             addSkip(self, reason)
         else:
-            warnings.warn("Use of a TestResult without an addSkip method is deprecated",
-                          DeprecationWarning, 2)
+            warnings.warn("TestResult has no addSkip method, skips not reported",
+                          RuntimeWarning, 2)
             result.addSuccess(self)
 
-    def _addExpectedFailure(self, result, exc_info):
-        addExpectedFailure = getattr(result, 'addExpectedFailure', None)
-        if addExpectedFailure is not None:
-            addExpectedFailure(self, exc_info)
-        else:
-            warnings.warn("Use of a TestResult without an addExpectedFailure method is deprecated",
-                          DeprecationWarning)
-            result.addSuccess(self)
+    def _executeTestPart(self, function, outcome, isTest=False):
+        try:
+            function()
+        except KeyboardInterrupt:
+            raise
+        except SkipTest, e:
+            outcome.success = False
+            outcome.skipped = str(e)
+        except _UnexpectedSuccess:
+            exc_info = sys.exc_info()
+            outcome.success = False
+            if isTest:
+                outcome.unexpectedSuccess = exc_info
+            else:
+                outcome.errors.append(exc_info)
+        except _ExpectedFailure:
+            outcome.success = False
+            exc_info = sys.exc_info()
+            if isTest:
+                outcome.expectedFailure = exc_info
+            else:
+                outcome.errors.append(exc_info)
+        except self.failureException:
+            outcome.success = False
+            outcome.failures.append(sys.exc_info())
+            exc_info = sys.exc_info()
+        except:
+            outcome.success = False
+            outcome.errors.append(sys.exc_info())
 
     def run(self, result=None):
         orig_result = result
             if startTestRun is not None:
                 startTestRun()
 
-        self._resultForDoCleanups = result
         result.startTest(self)
 
         testMethod = getattr(self, self._testMethodName)
-
         if (getattr(self.__class__, "__unittest_skip__", False) or
             getattr(testMethod, "__unittest_skip__", False)):
             # If the class or method was skipped.
                 result.stopTest(self)
             return
         try:
-            success = False
-            try:
-                self.setUp()
-            except SkipTest, e:
-                self._addSkip(result, str(e))
-            except Exception:
-                exc_info = sys.exc_info()
-                if getattr(testMethod, '_expectedFailure', None) is not None:
-                    self._addExpectedFailure(result, exc_info)
-                else:
+            outcome = _Outcome()
+            self._outcomeForDoCleanups = outcome
+
+            self._executeTestPart(self.setUp, outcome)
+            if outcome.success:
+                self._executeTestPart(testMethod, outcome, isTest=True)
+                self._executeTestPart(self.tearDown, outcome)
+
+            self.doCleanups()
+            if outcome.success:
+                result.addSuccess(self)
+            else:
+                if outcome.skipped is not None:
+                    self._addSkip(result, outcome.skipped)
+                for exc_info in outcome.errors:
                     result.addError(self, exc_info)
-            else:
-                try:
-                    testMethod()
-                except self.failureException:
-                    result.addFailure(self, sys.exc_info())
-                except _ExpectedFailure:
-                    self._addExpectedFailure(result, sys.exc_info())
-                except _UnexpectedSuccess:
+                for exc_info in outcome.failures:
+                    result.addFailure(self, exc_info)
+                if outcome.unexpectedSuccess is not None:
                     addUnexpectedSuccess = getattr(result, 'addUnexpectedSuccess', None)
                     if addUnexpectedSuccess is not None:
                         addUnexpectedSuccess(self)
                     else:
-                        warnings.warn("Use of a TestResult without an addUnexpectedSuccess method is deprecated",
-                                      DeprecationWarning)
-                        result.addFailure(self, sys.exc_info())
-                except SkipTest, e:
-                    self._addSkip(result, str(e))
-                except Exception:
-                    result.addError(self, sys.exc_info())
-                else:
-                    success = True
+                        warnings.warn("TestResult has no addUnexpectedSuccess method, reporting as failures",
+                                      RuntimeWarning)
+                        result.addFailure(self, outcome.unexpectedSuccess)
 
-                try:
-                    self.tearDown()
-                except Exception:
-                    exc_info = sys.exc_info()
-                    if getattr(testMethod, '_expectedFailure', None) is not None:
-                        self._addExpectedFailure(result, exc_info)
+                if outcome.expectedFailure is not None:
+                    addExpectedFailure = getattr(result, 'addExpectedFailure', None)
+                    if addExpectedFailure is not None:
+                        addExpectedFailure(self, outcome.expectedFailure)
                     else:
-                        result.addError(self, exc_info)
-                        success = False
-
-            cleanUpSuccess = self.doCleanups()
-            success = success and cleanUpSuccess
-            if success:
-                result.addSuccess(self)
+                        warnings.warn("TestResult has no addExpectedFailure method, reporting as passes",
+                                      RuntimeWarning)
+                        result.addSuccess(self)
+            return result
         finally:
             result.stopTest(self)
             if orig_result is None:
     def doCleanups(self):
         """Execute all cleanup functions. Normally called for you after
         tearDown."""
-        result = self._resultForDoCleanups
-        ok = True
+        outcome = self._outcomeForDoCleanups or _Outcome()
         while self._cleanups:
-            function, args, kwargs = self._cleanups.pop(-1)
-            try:
-                function(*args, **kwargs)
-            except Exception:
-                ok = False
-                result.addError(self, sys.exc_info())
-        return ok
+            function, args, kwargs = self._cleanups.pop()
+            part = lambda: function(*args, **kwargs)
+            self._executeTestPart(part, outcome)
+
+        # return this for backwards compatibility
+        # even though we no longer us it internally
+        return outcome.success
 
     def __call__(self, *args, **kwds):
         return self.run(*args, **kwds)
             return original_func(*args, **kwargs)
         return deprecated_func
 
-    # The fail* methods have been removed in 3.3, the 5 assert* methods will
-    # have to stay around for a few more versions.  See #9424.
     failUnlessEqual = assertEquals = _deprecate(assertEqual)
     failIfEqual = assertNotEquals = _deprecate(assertNotEqual)
     failUnlessAlmostEqual = assertAlmostEquals = _deprecate(assertAlmostEqual)

unittest2/test/_test_unittest2_with.py

     """Tests that use the with statement live in this
     module so that all other tests can be run with Python 2.4.
     """
+    def setUp(self):
+        self.foo = False
 
     def testAssertRaisesExcValue(self):
         class ExceptionMock(Exception):
             test.run(result)
             self.assertEqual(len(result.failures), failures)
             warning, = log
-            self.assertIs(warning.category, DeprecationWarning)
+            self.assertIs(warning.category, RuntimeWarning)
 
     def test_old_testresult(self):
         class Test(unittest2.TestCase):
             test = Test(test_name)
             self.assertOldResultWarning(test, int(not should_pass))
 
+
     def test_old_testresult_setup(self):
         class Test(unittest2.TestCase):
             def setUp(self):
                 self.skipTest('no reason')
             def testFoo(self):
                 pass
+        self.foo = True
         self.assertOldResultWarning(Test('testFoo'), 0)
 
     def test_old_testresult_class(self):

unittest2/test/test_case.py

             self.events.append('tearDown')
 
 
-
-class TestCleanUp(unittest2.TestCase):
-
-    def testCleanUp(self):
-        class TestableTest(unittest2.TestCase):
-            def testNothing(self):
-                pass
-
-        test = TestableTest('testNothing')
-        self.assertEqual(test._cleanups, [])
-
-        cleanups = []
-
-        def cleanup1(*args, **kwargs):
-            cleanups.append((1, args, kwargs))
-
-        def cleanup2(*args, **kwargs):
-            cleanups.append((2, args, kwargs))
-
-        test.addCleanup(cleanup1, 1, 2, 3, four='hello', five='goodbye')
-        test.addCleanup(cleanup2)
-
-        self.assertEqual(test._cleanups,
-                         [(cleanup1, (1, 2, 3), dict(four='hello', five='goodbye')),
-                          (cleanup2, (), {})])
-
-        result = test.doCleanups()
-        self.assertTrue(result)
-
-        self.assertEqual(cleanups, [(2, (), {}), (1, (1, 2, 3), dict(four='hello', five='goodbye'))])
-
-    def testCleanUpWithErrors(self):
-        class TestableTest(unittest2.TestCase):
-            def testNothing(self):
-                pass
-
-        class MockResult(object):
-            errors = []
-            def addError(self, test, exc_info):
-                self.errors.append((test, exc_info))
-
-        result = MockResult()
-        test = TestableTest('testNothing')
-        test._resultForDoCleanups = result
-
-        exc1 = Exception('foo')
-        exc2 = Exception('bar')
-        def cleanup1():
-            raise exc1
-
-        def cleanup2():
-            raise exc2
-
-        test.addCleanup(cleanup1)
-        test.addCleanup(cleanup2)
-
-        self.assertFalse(test.doCleanups())
-
-        (test1, (Type1, instance1, _)), (test2, (Type2, instance2, _)) = reversed(MockResult.errors)
-        self.assertEqual((test1, Type1, instance1), (test, Exception, exc1))
-        self.assertEqual((test2, Type2, instance2), (test, Exception, exc2))
-
-    def testCleanupInRun(self):
-        blowUp = False
-        ordering = []
-
-        class TestableTest(unittest2.TestCase):
-            def setUp(self):
-                ordering.append('setUp')
-                if blowUp:
-                    raise Exception('foo')
-
-            def testNothing(self):
-                ordering.append('test')
-
-            def tearDown(self):
-                ordering.append('tearDown')
-
-        test = TestableTest('testNothing')
-
-        def cleanup1():
-            ordering.append('cleanup1')
-        def cleanup2():
-            ordering.append('cleanup2')
-        test.addCleanup(cleanup1)
-        test.addCleanup(cleanup2)
-
-        def success(some_test):
-            self.assertEqual(some_test, test)
-            ordering.append('success')
-
-        result = unittest2.TestResult()
-        result.addSuccess = success
-
-        test.run(result)
-        self.assertEqual(ordering, ['setUp', 'test', 'tearDown',
-                                    'cleanup2', 'cleanup1', 'success'])
-
-        blowUp = True
-        ordering = []
-        test = TestableTest('testNothing')
-        test.addCleanup(cleanup1)
-        test.run(result)
-        self.assertEqual(ordering, ['setUp', 'cleanup1'])
-
-    def testTestCaseDebugExecutesCleanups(self):
-        ordering = []
-
-        class TestableTest(unittest2.TestCase):
-            def setUp(self):
-                ordering.append('setUp')
-                self.addCleanup(cleanup1)
-
-            def testNothing(self):
-                ordering.append('test')
-
-            def tearDown(self):
-                ordering.append('tearDown')
-
-        test = TestableTest('testNothing')
-
-        def cleanup1():
-            ordering.append('cleanup1')
-            test.addCleanup(cleanup2)
-        def cleanup2():
-            ordering.append('cleanup2')
-
-        test.debug()
-        self.assertEqual(ordering, ['setUp', 'test', 'tearDown', 'cleanup1', 'cleanup2'])
-
-
 class Test_TestCase(unittest2.TestCase, EqualityMixin, HashingMixin):
 
     ### Set up attributes used by inherited tests
                 super(Foo, self).test()
                 raise RuntimeError('raised by Foo.test')
 
-        expected = ['startTest', 'setUp', 'test', 'addError', 'tearDown',
+        expected = ['startTest', 'setUp', 'test', 'tearDown', 'addError',
                     'stopTest']
         Foo(events).run(result)
         self.assertEqual(events, expected)
                 super(Foo, self).test()
                 raise RuntimeError('raised by Foo.test')
 
-        expected = ['startTestRun', 'startTest', 'setUp', 'test', 'addError',
-                    'tearDown', 'stopTest', 'stopTestRun']
+        expected = ['startTestRun', 'startTest', 'setUp', 'test',
+                    'tearDown', 'addError', 'stopTest', 'stopTestRun']
         Foo(events).run()
         self.assertEqual(events, expected)
 
                 super(Foo, self).test()
                 self.fail('raised by Foo.test')
 
-        expected = ['startTest', 'setUp', 'test', 'addFailure', 'tearDown',
+        expected = ['startTest', 'setUp', 'test', 'tearDown', 'addFailure',
                     'stopTest']
         Foo(events).run(result)
         self.assertEqual(events, expected)
                 super(Foo, self).test()
                 self.fail('raised by Foo.test')
 
-        expected = ['startTestRun', 'startTest', 'setUp', 'test', 'addFailure',
-                    'tearDown', 'stopTest', 'stopTestRun']
+        expected = ['startTestRun', 'startTest', 'setUp', 'test',
+                    'tearDown', 'addFailure', 'stopTest', 'stopTestRun']
         events = []
         Foo(events).run()
         self.assertEqual(events, expected)
             unpickled_test.assertEqual(set(), set())
 
 
-    def testExpectedFailureInSetUp(self):
-        class Test(unittest2.TestCase):
-            def setUp(self):
-                1/0
-            @unittest2.expectedFailure
+    def testKeyboardInterrupt(self):
+        def _raise(self=None):
+            raise KeyboardInterrupt
+        def nothing(self):
+            pass
+
+        class Test1(unittest2.TestCase):
+            test_something = _raise
+
+        class Test2(unittest2.TestCase):
+            setUp = _raise
+            test_something = nothing
+
+        class Test3(unittest2.TestCase):
+            test_something = nothing
+            tearDown = _raise
+
+        class Test4(unittest2.TestCase):
             def test_something(self):
-                pass
+                self.addCleanup(_raise)
 
-        test = Test('test_something')
+        for klass in (Test1, Test2, Test3, Test4):
+            with self.assertRaises(KeyboardInterrupt):
+                klass('test_something').run()
 
-        result = unittest2.TestResult()
-        test(result)
+    def testSkippingEverywhere(self):
+        def _skip(self=None):
+            raise unittest2.SkipTest('some reason')
+        def nothing(self):
+            pass
 
-        self.assertEqual(len(result.errors), 0)
-        self.assertEqual(len(result.failures), 0)
-        self.assertEqual(len(result.expectedFailures), 1)
-        self.assertEqual(result.testsRun, 1)
+        class Test1(unittest2.TestCase):
+            test_something = _skip
 
-        result = OldTestResult()
-        test(result)
-        self.assertEqual(len(result.errors), 0)
-        self.assertEqual(len(result.failures), 0)
-        self.assertEqual(result.testsRun, 1)
+        class Test2(unittest2.TestCase):
+            setUp = _skip
+            test_something = nothing
 
+        class Test3(unittest2.TestCase):
+            test_something = nothing
+            tearDown = _skip
 
-    def testExpectedFailureInTearDown(self):
-        class Test(unittest2.TestCase):
-            def tearDown(self):
-                1/0
-            @unittest2.expectedFailure
+        class Test4(unittest2.TestCase):
             def test_something(self):
-                pass
+                self.addCleanup(_skip)
 
-        test = Test('test_something')
+        for klass in (Test1, Test2, Test3, Test4):
+            result = unittest2.TestResult()
+            klass('test_something').run(result)
+            self.assertEqual(len(result.skipped), 1)
+            self.assertEqual(result.testsRun, 1)
 
-        result = unittest2.TestResult()
-        test(result)
+    def testSystemExit(self):
+        def _raise(self=None):
+            raise SystemExit
+        def nothing(self):
+            pass
 
-        self.assertEqual(len(result.errors), 0)
-        self.assertEqual(len(result.failures), 0)
-        self.assertEqual(len(result.expectedFailures), 1)
-        self.assertEqual(len(result.unexpectedSuccesses), 0)
-        self.assertEqual(result.testsRun, 1)
+        class Test1(unittest2.TestCase):
+            test_something = _raise
 
-        result = OldTestResult()
-        test(result)
-        self.assertEqual(len(result.errors), 0)
-        self.assertEqual(len(result.failures), 0)
-        self.assertEqual(result.testsRun, 1)
+        class Test2(unittest2.TestCase):
+            setUp = _raise
+            test_something = nothing
 
+        class Test3(unittest2.TestCase):
+            test_something = nothing
+            tearDown = _raise
 
-    def testExpectedFailureInCleanUp(self):
-        class Test(unittest2.TestCase):
-            @unittest2.expectedFailure
+        class Test4(unittest2.TestCase):
             def test_something(self):
-                def foo():
-                    1/0
-                self.addCleanup(foo)
+                self.addCleanup(_raise)
 
-        test = Test('test_something')
-
-        result = unittest2.TestResult()
-        test(result)
-
-        self.assertEqual(len(result.errors), 0)
-        self.assertEqual(len(result.failures), 0)
-        self.assertEqual(len(result.expectedFailures), 1)
-        self.assertEqual(len(result.unexpectedSuccesses), 0)
-        self.assertEqual(result.testsRun, 1)
-
-        result = OldTestResult()
-        test(result)
-        self.assertEqual(len(result.errors), 0)
-        self.assertEqual(len(result.failures), 0)
-        self.assertEqual(result.testsRun, 1)
+        for klass in (Test1, Test2, Test3, Test4):
+            result = unittest2.TestResult()
+            klass('test_something').run(result)
+            self.assertEqual(len(result.errors), 1)
+            self.assertEqual(result.testsRun, 1)
 
 
 if __name__ == "__main__":

unittest2/test/test_functiontestcase.py

         def tearDown():
             events.append('tearDown')
 
-        expected = ['startTest', 'setUp', 'test', 'addError', 'tearDown',
+        expected = ['startTest', 'setUp', 'test', 'tearDown', 'addError',
                     'stopTest']
         unittest2.FunctionTestCase(test, setUp, tearDown).run(result)
         self.assertEqual(events, expected)
         def tearDown():
             events.append('tearDown')
 
-        expected = ['startTest', 'setUp', 'test', 'addFailure', 'tearDown',
+        expected = ['startTest', 'setUp', 'test', 'tearDown', 'addFailure',
                     'stopTest']
         unittest2.FunctionTestCase(test, setUp, tearDown).run(result)
         self.assertEqual(events, expected)

unittest2/test/test_runner.py

 import unittest2
 
 
+class TestCleanUp(unittest2.TestCase):
+
+    def testCleanUp(self):
+        class TestableTest(unittest2.TestCase):
+            def testNothing(self):
+                pass
+
+        test = TestableTest('testNothing')
+        self.assertEqual(test._cleanups, [])
+
+        cleanups = []
+
+        def cleanup1(*args, **kwargs):
+            cleanups.append((1, args, kwargs))
+
+        def cleanup2(*args, **kwargs):
+            cleanups.append((2, args, kwargs))
+
+        test.addCleanup(cleanup1, 1, 2, 3, four='hello', five='goodbye')
+        test.addCleanup(cleanup2)
+
+        self.assertEqual(test._cleanups,
+                         [(cleanup1, (1, 2, 3), dict(four='hello', five='goodbye')),
+                          (cleanup2, (), {})])
+
+        result = test.doCleanups()
+        self.assertTrue(result)
+
+        self.assertEqual(cleanups, [(2, (), {}), (1, (1, 2, 3),
+                                     dict(four='hello', five='goodbye'))])
+
+    def testCleanUpWithErrors(self):
+        class TestableTest(unittest2.TestCase):
+            def testNothing(self):
+                pass
+
+        class MockOutcome(object):
+            success = True
+            errors = []
+
+        test = TestableTest('testNothing')
+        test._outcomeForDoCleanups = MockOutcome
+
+        exc1 = Exception('foo')
+        exc2 = Exception('bar')
+        def cleanup1():
+            raise exc1
+
+        def cleanup2():
+            raise exc2
+
+        test.addCleanup(cleanup1)
+        test.addCleanup(cleanup2)
+
+        self.assertFalse(test.doCleanups())
+        self.assertFalse(MockOutcome.success)
+
+        (Type1, instance1, _), (Type2, instance2, _) = reversed(MockOutcome.errors)
+        self.assertEqual((Type1, instance1), (Exception, exc1))
+        self.assertEqual((Type2, instance2), (Exception, exc2))
+
+    def testCleanupInRun(self):
+        blowUp = False
+        ordering = []
+
+        class TestableTest(unittest2.TestCase):
+            def setUp(self):
+                ordering.append('setUp')
+                if blowUp:
+                    raise Exception('foo')
+
+            def testNothing(self):
+                ordering.append('test')
+
+            def tearDown(self):
+                ordering.append('tearDown')
+
+        test = TestableTest('testNothing')
+
+        def cleanup1():
+            ordering.append('cleanup1')
+        def cleanup2():
+            ordering.append('cleanup2')
+        test.addCleanup(cleanup1)
+        test.addCleanup(cleanup2)
+
+        def success(some_test):
+            self.assertEqual(some_test, test)
+            ordering.append('success')
+
+        result = unittest2.TestResult()
+        result.addSuccess = success
+
+        test.run(result)
+        self.assertEqual(ordering, ['setUp', 'test', 'tearDown',
+                                    'cleanup2', 'cleanup1', 'success'])
+
+        blowUp = True
+        ordering = []
+        test = TestableTest('testNothing')
+        test.addCleanup(cleanup1)
+        test.run(result)
+        self.assertEqual(ordering, ['setUp', 'cleanup1'])
+
+    def testTestCaseDebugExecutesCleanups(self):
+        ordering = []
+
+        class TestableTest(unittest2.TestCase):
+            def setUp(self):
+                ordering.append('setUp')
+                self.addCleanup(cleanup1)
+
+            def testNothing(self):
+                ordering.append('test')
+
+            def tearDown(self):
+                ordering.append('tearDown')
+
+        test = TestableTest('testNothing')
+
+        def cleanup1():
+            ordering.append('cleanup1')
+            test.addCleanup(cleanup2)
+        def cleanup2():
+            ordering.append('cleanup2')
+
+        test.debug()
+        self.assertEqual(ordering, ['setUp', 'test', 'tearDown', 'cleanup1', 'cleanup2'])
+
+
 class Test_TextTestRunner(unittest2.TestCase):
     """Tests for TextTestRunner."""