From 53e8eeadd60ab7566d2bc29a51ce286a890dd14b Mon Sep 17 00:00:00 2001 From: Michael Foord Date: Sun, 7 Mar 2010 20:22:12 +0000 Subject: [PATCH] Fix for potentials errors in constructing unittest failure messages. Plus skipped test methods no longer run setUp and tearDown (Issue 8059) --- Lib/test/test_unittest.py | 47 +++++++++++++++++++++++++++++++++++++++ Lib/unittest/case.py | 36 +++++++++++++++++++----------- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/Lib/test/test_unittest.py b/Lib/test/test_unittest.py index 048f67b0971..be4811ccf51 100644 --- a/Lib/test/test_unittest.py +++ b/Lib/test/test_unittest.py @@ -3118,6 +3118,43 @@ class Test_TestSkipping(TestCase): self.assertEqual(result.unexpectedSuccesses, [test]) self.assertTrue(result.wasSuccessful()) + def test_skip_doesnt_run_setup(self): + class Foo(unittest.TestCase): + wasSetUp = False + wasTornDown = False + def setUp(self): + Foo.wasSetUp = True + def tornDown(self): + Foo.wasTornDown = True + @unittest.skip('testing') + def test_1(self): + pass + + result = unittest.TestResult() + test = Foo("test_1") + suite = unittest.TestSuite([test]) + suite.run(result) + self.assertEqual(result.skipped, [(test, "testing")]) + self.assertFalse(Foo.wasSetUp) + self.assertFalse(Foo.wasTornDown) + + def test_decorated_skip(self): + def decorator(func): + def inner(*a): + return func(*a) + return inner + + class Foo(unittest.TestCase): + @decorator + @unittest.skip('testing') + def test_1(self): + pass + + result = unittest.TestResult() + test = Foo("test_1") + suite = unittest.TestSuite([test]) + suite.run(result) + self.assertEqual(result.skipped, [(test, "testing")]) class Test_Assertions(TestCase): @@ -3220,6 +3257,16 @@ class TestLongMessage(TestCase): self.assertEquals(self.testableTrue._formatMessage(None, "foo"), "foo") self.assertEquals(self.testableTrue._formatMessage("foo", "bar"), "bar : foo") + # This blows up if _formatMessage uses string concatenation + self.testableTrue._formatMessage(object(), 'foo') + + def test_formatMessage_unicode_error(self): + with warnings.catch_warnings(record=True): + # This causes a UnicodeWarning due to its craziness + one = ''.join(chr(i) for i in range(255)) + # this used to cause a UnicodeDecodeError constructing msg + self.testableTrue._formatMessage(one, u'\uFFFD') + def assertMessages(self, methodName, args, errors): def getMethod(i): useTestableFalse = i < 2 diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index aacd67ca087..19b196c86fe 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -45,14 +45,15 @@ def skip(reason): Unconditionally skip a test. """ def decorator(test_item): - if isinstance(test_item, type) and issubclass(test_item, TestCase): - test_item.__unittest_skip__ = True - test_item.__unittest_skip_why__ = reason - return test_item - @functools.wraps(test_item) - def skip_wrapper(*args, **kwargs): - raise SkipTest(reason) - return skip_wrapper + if not (isinstance(test_item, type) and issubclass(test_item, TestCase)): + @functools.wraps(test_item) + def skip_wrapper(*args, **kwargs): + raise SkipTest(reason) + test_item = skip_wrapper + + test_item.__unittest_skip__ = True + test_item.__unittest_skip_why__ = reason + return test_item return decorator def skipIf(condition, reason): @@ -268,14 +269,18 @@ class TestCase(object): self._resultForDoCleanups = result result.startTest(self) - if getattr(self.__class__, "__unittest_skip__", False): - # If the whole class was skipped. + + 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. try: - self._addSkip(result, self.__class__.__unittest_skip_why__) + skip_why = (getattr(self.__class__, '__unittest_skip_why__', '') + or getattr(testMethod, '__unittest_skip_why__', '')) + self._addSkip(result, skip_why) finally: result.stopTest(self) return - testMethod = getattr(self, self._testMethodName) try: success = False try: @@ -386,7 +391,12 @@ class TestCase(object): return msg or standardMsg if msg is None: return standardMsg - return standardMsg + ' : ' + msg + try: + # don't switch to '{}' formatting in Python 2.X + # it changes the way unicode input is handled + return '%s : %s' % (standardMsg, msg) + except UnicodeDecodeError: + return '%s : %s' % (safe_repr(standardMsg), safe_repr(msg)) def assertRaises(self, excClass, callableObj=None, *args, **kwargs):