Merged revisions 78227,78229,78288,78348,78377,78770,78774-78776,78810 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/trunk

........
  r78227 | michael.foord | 2010-02-18 14:30:09 -0600 (Thu, 18 Feb 2010) | 1 line

  unittest.TestCase uses safe_repr for producing failure messages. Partial fix for issue 7956
........
  r78229 | michael.foord | 2010-02-18 15:37:07 -0600 (Thu, 18 Feb 2010) | 1 line

  Fix unittest.TestCase.assertDictContainsSubset so it can't die with unicode issues when constructing failure messages. Issue 7956
........
  r78288 | michael.foord | 2010-02-21 08:48:59 -0600 (Sun, 21 Feb 2010) | 1 line

  Silence UnicodeWarning in crazy unittest test.
........
  r78348 | michael.foord | 2010-02-22 17:28:32 -0600 (Mon, 22 Feb 2010) | 1 line

  Support for old TestResult object (unittest) with warnings when using unsupported features.
........
  r78377 | michael.foord | 2010-02-23 11:00:53 -0600 (Tue, 23 Feb 2010) | 1 line

  unittest.TestResult can now be used with the TextTestRunner. TextTestRunner compatible with old TestResult objects.
........
  r78770 | michael.foord | 2010-03-07 14:22:12 -0600 (Sun, 07 Mar 2010) | 1 line

  Fix for potentials errors in constructing unittest failure messages. Plus skipped test methods no longer run setUp and tearDown (Issue 8059)
........
  r78774 | michael.foord | 2010-03-07 16:04:55 -0600 (Sun, 07 Mar 2010) | 1 line

  Addition of setUpClass and setUpModule shared fixtures to unittest.
........
  r78775 | michael.foord | 2010-03-07 17:10:36 -0600 (Sun, 07 Mar 2010) | 1 line

  Fix accidental name rebinding in unittest py3k warning filtering.
........
  r78776 | michael.foord | 2010-03-07 17:16:20 -0600 (Sun, 07 Mar 2010) | 1 line

  Remove accidental print statement from last commit.
........
  r78810 | raymond.hettinger | 2010-03-09 02:44:18 -0600 (Tue, 09 Mar 2010) | 5 lines

  Improve the basic example.
  * Show both the decorator and regular form for assertRaises()
  * Use assertTrue() instead of assertIn() to teach useful minimal subset of the API
........
This commit is contained in:
Benjamin Peterson 2010-03-14 15:04:17 +00:00
parent 3b8bfeffb3
commit 847a4110ea
8 changed files with 865 additions and 94 deletions

View File

@ -177,14 +177,18 @@ Here is a short script to test three functions from the :mod:`random` module::
self.seq.sort() self.seq.sort()
self.assertEqual(self.seq, list(range(10))) self.assertEqual(self.seq, list(range(10)))
# should raise an exception for an immutable sequence
self.assertRaises(TypeError, random.shuffle, (1,2,3))
def test_choice(self): def test_choice(self):
element = random.choice(self.seq) element = random.choice(self.seq)
self.assertIn(element, self.seq) self.assertTrue(element in self.seq)
def test_sample(self): def test_sample(self):
self.assertRaises(ValueError, random.sample, self.seq, 20) with self.assertRaises(ValueError):
random.sample(self.seq, 20)
for element in random.sample(self.seq, 5): for element in random.sample(self.seq, 5):
self.assertIn(element, self.seq) self.assertTrue(element in self.seq)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -18,10 +18,15 @@ import types
from copy import deepcopy from copy import deepcopy
import io import io
import pickle import pickle
import warnings
### Support code ### Support code
################################################################ ################################################################
def resultFactory(*_):
return unittest.TestResult()
class LoggingResult(unittest.TestResult): class LoggingResult(unittest.TestResult):
def __init__(self, log): def __init__(self, log):
self._events = log self._events = log
@ -2076,6 +2081,70 @@ class Test_TestResult(TestCase):
'Tests getDescription() for a method with a longer ' 'Tests getDescription() for a method with a longer '
'docstring.')) 'docstring.'))
classDict = dict(unittest.TestResult.__dict__)
for m in ('addSkip', 'addExpectedFailure', 'addUnexpectedSuccess',
'__init__'):
del classDict[m]
def __init__(self, stream=None, descriptions=None, verbosity=None):
self.failures = []
self.errors = []
self.testsRun = 0
self.shouldStop = False
classDict['__init__'] = __init__
OldResult = type('OldResult', (object,), classDict)
class Test_OldTestResult(unittest.TestCase):
def assertOldResultWarning(self, test, failures):
with warnings.catch_warnings(record=True) as log:
result = OldResult()
test.run(result)
self.assertEqual(len(result.failures), failures)
warning, = log
self.assertIs(warning.category, RuntimeWarning)
def testOldTestResult(self):
class Test(unittest.TestCase):
def testSkip(self):
self.skipTest('foobar')
@unittest.expectedFailure
def testExpectedFail(self):
raise TypeError
@unittest.expectedFailure
def testUnexpectedSuccess(self):
pass
for test_name, should_pass in (('testSkip', True),
('testExpectedFail', True),
('testUnexpectedSuccess', False)):
test = Test(test_name)
self.assertOldResultWarning(test, int(not should_pass))
def testOldTestTesultSetup(self):
class Test(unittest.TestCase):
def setUp(self):
self.skipTest('no reason')
def testFoo(self):
pass
self.assertOldResultWarning(Test('testFoo'), 0)
def testOldTestResultClass(self):
@unittest.skip('no reason')
class Test(unittest.TestCase):
def testFoo(self):
pass
self.assertOldResultWarning(Test('testFoo'), 0)
def testOldResultWithRunner(self):
class Test(unittest.TestCase):
def testFoo(self):
pass
runner = unittest.TextTestRunner(resultclass=OldResult,
stream=io.StringIO())
# This will raise an exception if TextTestRunner can't handle old
# test result objects
runner.run(Test('testFoo'))
### Support code for Test_TestCase ### Support code for Test_TestCase
################################################################ ################################################################
@ -2578,21 +2647,27 @@ class Test_TestCase(TestCase, TestEquality, TestHashing):
self.assertDictContainsSubset({'a': 1}, {'a': 1, 'b': 2}) self.assertDictContainsSubset({'a': 1}, {'a': 1, 'b': 2})
self.assertDictContainsSubset({'a': 1, 'b': 2}, {'a': 1, 'b': 2}) self.assertDictContainsSubset({'a': 1, 'b': 2}, {'a': 1, 'b': 2})
self.assertRaises(unittest.TestCase.failureException, with self.assertRaises(self.failureException):
self.assertDictContainsSubset, {'a': 2}, {'a': 1}, self.assertDictContainsSubset({1: "one"}, {})
'.*Mismatched values:.*')
self.assertRaises(unittest.TestCase.failureException, with self.assertRaises(self.failureException):
self.assertDictContainsSubset, {'c': 1}, {'a': 1}, self.assertDictContainsSubset({'a': 2}, {'a': 1})
'.*Missing:.*')
self.assertRaises(unittest.TestCase.failureException, with self.assertRaises(self.failureException):
self.assertDictContainsSubset, {'a': 1, 'c': 1}, self.assertDictContainsSubset({'c': 1}, {'a': 1})
{'a': 1}, '.*Missing:.*')
self.assertRaises(unittest.TestCase.failureException, with self.assertRaises(self.failureException):
self.assertDictContainsSubset, {'a': 1, 'c': 1}, self.assertDictContainsSubset({'a': 1, 'c': 1}, {'a': 1})
{'a': 1}, '.*Missing:.*Mismatched values:.*')
with self.assertRaises(self.failureException):
self.assertDictContainsSubset({'a': 1, 'c': 1}, {'a': 1})
with warnings.catch_warnings(record=True):
# silence the UnicodeWarning
one = ''.join(chr(i) for i in range(255))
# this used to cause a UnicodeDecodeError constructing the failure msg
with self.assertRaises(self.failureException):
self.assertDictContainsSubset({'foo': one}, {'foo': '\uFFFD'})
def testAssertEqual(self): def testAssertEqual(self):
equal_pairs = [ equal_pairs = [
@ -3028,6 +3103,43 @@ class Test_TestSkipping(TestCase):
self.assertEqual(result.unexpectedSuccesses, [test]) self.assertEqual(result.unexpectedSuccesses, [test])
self.assertTrue(result.wasSuccessful()) 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): class Test_Assertions(TestCase):
@ -3131,6 +3243,16 @@ class TestLongMessage(TestCase):
self.assertEquals(self.testableTrue._formatMessage(None, "foo"), "foo") self.assertEquals(self.testableTrue._formatMessage(None, "foo"), "foo")
self.assertEquals(self.testableTrue._formatMessage("foo", "bar"), "bar : 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, '\uFFFD')
def assertMessages(self, methodName, args, errors): def assertMessages(self, methodName, args, errors):
def getMethod(i): def getMethod(i):
useTestableFalse = i < 2 useTestableFalse = i < 2
@ -3795,6 +3917,397 @@ class TestDiscovery(TestCase):
self.assertEqual(program.verbosity, 2) self.assertEqual(program.verbosity, 2)
class TestSetups(unittest.TestCase):
def getRunner(self):
return unittest.TextTestRunner(resultclass=resultFactory,
stream=io.StringIO())
def runTests(self, *cases):
suite = unittest.TestSuite()
for case in cases:
tests = unittest.defaultTestLoader.loadTestsFromTestCase(case)
suite.addTests(tests)
runner = self.getRunner()
# creating a nested suite exposes some potential bugs
realSuite = unittest.TestSuite()
realSuite.addTest(suite)
# adding empty suites to the end exposes potential bugs
suite.addTest(unittest.TestSuite())
realSuite.addTest(unittest.TestSuite())
return runner.run(realSuite)
def test_setup_class(self):
class Test(unittest.TestCase):
setUpCalled = 0
@classmethod
def setUpClass(cls):
Test.setUpCalled += 1
unittest.TestCase.setUpClass()
def test_one(self):
pass
def test_two(self):
pass
result = self.runTests(Test)
self.assertEqual(Test.setUpCalled, 1)
self.assertEqual(result.testsRun, 2)
self.assertEqual(len(result.errors), 0)
def test_teardown_class(self):
class Test(unittest.TestCase):
tearDownCalled = 0
@classmethod
def tearDownClass(cls):
Test.tearDownCalled += 1
unittest.TestCase.tearDownClass()
def test_one(self):
pass
def test_two(self):
pass
result = self.runTests(Test)
self.assertEqual(Test.tearDownCalled, 1)
self.assertEqual(result.testsRun, 2)
self.assertEqual(len(result.errors), 0)
def test_teardown_class_two_classes(self):
class Test(unittest.TestCase):
tearDownCalled = 0
@classmethod
def tearDownClass(cls):
Test.tearDownCalled += 1
unittest.TestCase.tearDownClass()
def test_one(self):
pass
def test_two(self):
pass
class Test2(unittest.TestCase):
tearDownCalled = 0
@classmethod
def tearDownClass(cls):
Test2.tearDownCalled += 1
unittest.TestCase.tearDownClass()
def test_one(self):
pass
def test_two(self):
pass
result = self.runTests(Test, Test2)
self.assertEqual(Test.tearDownCalled, 1)
self.assertEqual(Test2.tearDownCalled, 1)
self.assertEqual(result.testsRun, 4)
self.assertEqual(len(result.errors), 0)
def test_error_in_setupclass(self):
class BrokenTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
raise TypeError('foo')
def test_one(self):
pass
def test_two(self):
pass
result = self.runTests(BrokenTest)
self.assertEqual(result.testsRun, 0)
self.assertEqual(len(result.errors), 1)
error, _ = result.errors[0]
self.assertEqual(str(error),
'classSetUp (%s.BrokenTest)' % __name__)
def test_error_in_teardown_class(self):
class Test(unittest.TestCase):
tornDown = 0
@classmethod
def tearDownClass(cls):
Test.tornDown += 1
raise TypeError('foo')
def test_one(self):
pass
def test_two(self):
pass
class Test2(unittest.TestCase):
tornDown = 0
@classmethod
def tearDownClass(cls):
Test2.tornDown += 1
raise TypeError('foo')
def test_one(self):
pass
def test_two(self):
pass
result = self.runTests(Test, Test2)
self.assertEqual(result.testsRun, 4)
self.assertEqual(len(result.errors), 2)
self.assertEqual(Test.tornDown, 1)
self.assertEqual(Test2.tornDown, 1)
error, _ = result.errors[0]
self.assertEqual(str(error),
'classTearDown (%s.Test)' % __name__)
def test_class_not_torndown_when_setup_fails(self):
class Test(unittest.TestCase):
tornDown = False
@classmethod
def setUpClass(cls):
raise TypeError
@classmethod
def tearDownClass(cls):
Test.tornDown = True
raise TypeError('foo')
def test_one(self):
pass
self.runTests(Test)
self.assertFalse(Test.tornDown)
def test_class_not_setup_or_torndown_when_skipped(self):
class Test(unittest.TestCase):
classSetUp = False
tornDown = False
@classmethod
def setUpClass(cls):
Test.classSetUp = True
@classmethod
def tearDownClass(cls):
Test.tornDown = True
def test_one(self):
pass
Test = unittest.skip("hop")(Test)
self.runTests(Test)
self.assertFalse(Test.classSetUp)
self.assertFalse(Test.tornDown)
def test_setup_teardown_order_with_pathological_suite(self):
results = []
class Module1(object):
@staticmethod
def setUpModule():
results.append('Module1.setUpModule')
@staticmethod
def tearDownModule():
results.append('Module1.tearDownModule')
class Module2(object):
@staticmethod
def setUpModule():
results.append('Module2.setUpModule')
@staticmethod
def tearDownModule():
results.append('Module2.tearDownModule')
class Test1(unittest.TestCase):
@classmethod
def setUpClass(cls):
results.append('setup 1')
@classmethod
def tearDownClass(cls):
results.append('teardown 1')
def testOne(self):
results.append('Test1.testOne')
def testTwo(self):
results.append('Test1.testTwo')
class Test2(unittest.TestCase):
@classmethod
def setUpClass(cls):
results.append('setup 2')
@classmethod
def tearDownClass(cls):
results.append('teardown 2')
def testOne(self):
results.append('Test2.testOne')
def testTwo(self):
results.append('Test2.testTwo')
class Test3(unittest.TestCase):
@classmethod
def setUpClass(cls):
results.append('setup 3')
@classmethod
def tearDownClass(cls):
results.append('teardown 3')
def testOne(self):
results.append('Test3.testOne')
def testTwo(self):
results.append('Test3.testTwo')
Test1.__module__ = Test2.__module__ = 'Module'
Test3.__module__ = 'Module2'
sys.modules['Module'] = Module1
sys.modules['Module2'] = Module2
first = unittest.TestSuite((Test1('testOne'),))
second = unittest.TestSuite((Test1('testTwo'),))
third = unittest.TestSuite((Test2('testOne'),))
fourth = unittest.TestSuite((Test2('testTwo'),))
fifth = unittest.TestSuite((Test3('testOne'),))
sixth = unittest.TestSuite((Test3('testTwo'),))
suite = unittest.TestSuite((first, second, third, fourth, fifth, sixth))
runner = self.getRunner()
result = runner.run(suite)
self.assertEqual(result.testsRun, 6)
self.assertEqual(len(result.errors), 0)
self.assertEqual(results,
['Module1.setUpModule', 'setup 1',
'Test1.testOne', 'Test1.testTwo', 'teardown 1',
'setup 2', 'Test2.testOne', 'Test2.testTwo',
'teardown 2', 'Module1.tearDownModule',
'Module2.setUpModule', 'setup 3',
'Test3.testOne', 'Test3.testTwo',
'teardown 3', 'Module2.tearDownModule'])
def test_setup_module(self):
class Module(object):
moduleSetup = 0
@staticmethod
def setUpModule():
Module.moduleSetup += 1
class Test(unittest.TestCase):
def test_one(self):
pass
def test_two(self):
pass
Test.__module__ = 'Module'
sys.modules['Module'] = Module
result = self.runTests(Test)
self.assertEqual(Module.moduleSetup, 1)
self.assertEqual(result.testsRun, 2)
self.assertEqual(len(result.errors), 0)
def test_error_in_setup_module(self):
class Module(object):
moduleSetup = 0
moduleTornDown = 0
@staticmethod
def setUpModule():
Module.moduleSetup += 1
raise TypeError('foo')
@staticmethod
def tearDownModule():
Module.moduleTornDown += 1
class Test(unittest.TestCase):
classSetUp = False
classTornDown = False
@classmethod
def setUpClass(cls):
Test.classSetUp = True
@classmethod
def tearDownClass(cls):
Test.classTornDown = True
def test_one(self):
pass
def test_two(self):
pass
class Test2(unittest.TestCase):
def test_one(self):
pass
def test_two(self):
pass
Test.__module__ = 'Module'
Test2.__module__ = 'Module'
sys.modules['Module'] = Module
result = self.runTests(Test, Test2)
self.assertEqual(Module.moduleSetup, 1)
self.assertEqual(Module.moduleTornDown, 0)
self.assertEqual(result.testsRun, 0)
self.assertFalse(Test.classSetUp)
self.assertFalse(Test.classTornDown)
self.assertEqual(len(result.errors), 1)
error, _ = result.errors[0]
self.assertEqual(str(error), 'setUpModule (Module)')
def test_testcase_with_missing_module(self):
class Test(unittest.TestCase):
def test_one(self):
pass
def test_two(self):
pass
Test.__module__ = 'Module'
sys.modules.pop('Module', None)
result = self.runTests(Test)
self.assertEqual(result.testsRun, 2)
def test_teardown_module(self):
class Module(object):
moduleTornDown = 0
@staticmethod
def tearDownModule():
Module.moduleTornDown += 1
class Test(unittest.TestCase):
def test_one(self):
pass
def test_two(self):
pass
Test.__module__ = 'Module'
sys.modules['Module'] = Module
result = self.runTests(Test)
self.assertEqual(Module.moduleTornDown, 1)
self.assertEqual(result.testsRun, 2)
self.assertEqual(len(result.errors), 0)
def test_error_in_teardown_module(self):
class Module(object):
moduleTornDown = 0
@staticmethod
def tearDownModule():
Module.moduleTornDown += 1
raise TypeError('foo')
class Test(unittest.TestCase):
classSetUp = False
classTornDown = False
@classmethod
def setUpClass(cls):
Test.classSetUp = True
@classmethod
def tearDownClass(cls):
Test.classTornDown = True
def test_one(self):
pass
def test_two(self):
pass
class Test2(unittest.TestCase):
def test_one(self):
pass
def test_two(self):
pass
Test.__module__ = 'Module'
Test2.__module__ = 'Module'
sys.modules['Module'] = Module
result = self.runTests(Test, Test2)
self.assertEqual(Module.moduleTornDown, 1)
self.assertEqual(result.testsRun, 4)
self.assertTrue(Test.classSetUp)
self.assertTrue(Test.classTornDown)
self.assertEqual(len(result.errors), 1)
error, _ = result.errors[0]
self.assertEqual(str(error), 'tearDownModule (Module)')
###################################################################### ######################################################################
## Main ## Main
###################################################################### ######################################################################
@ -3803,7 +4316,8 @@ def test_main():
support.run_unittest(Test_TestCase, Test_TestLoader, support.run_unittest(Test_TestCase, Test_TestLoader,
Test_TestSuite, Test_TestResult, Test_FunctionTestCase, Test_TestSuite, Test_TestResult, Test_FunctionTestCase,
Test_TestSkipping, Test_Assertions, TestLongMessage, Test_TestSkipping, Test_Assertions, TestLongMessage,
Test_TestProgram, TestCleanUp, TestDiscovery, Test_TextTestRunner) Test_TestProgram, TestCleanUp, TestDiscovery, Test_TextTestRunner,
Test_OldTestResult, TestSetups)
if __name__ == "__main__": if __name__ == "__main__":
test_main() test_main()

View File

@ -51,13 +51,12 @@ __all__ = ['TestResult', 'TestCase', 'TestSuite',
# Expose obsolete functions for backwards compatibility # Expose obsolete functions for backwards compatibility
__all__.extend(['getTestCaseNames', 'makeSuite', 'findTestCases']) __all__.extend(['getTestCaseNames', 'makeSuite', 'findTestCases'])
__all__.append('_TextTestResult')
from .result import TestResult from .result import TestResult
from .case import (TestCase, FunctionTestCase, SkipTest, skip, skipIf, from .case import (TestCase, FunctionTestCase, SkipTest, skip, skipIf,
skipUnless, expectedFailure) skipUnless, expectedFailure)
from .suite import TestSuite from .suite import BaseTestSuite, TestSuite
from .loader import (TestLoader, defaultTestLoader, makeSuite, getTestCaseNames, from .loader import (TestLoader, defaultTestLoader, makeSuite, getTestCaseNames,
findTestCases) findTestCases)
from .main import TestProgram, main from .main import TestProgram, main

View File

@ -7,7 +7,9 @@ import pprint
import re import re
import warnings import warnings
from . import result, util from . import result
from .util import (strclass, safe_repr, sorted_list_difference,
unorderable_list_difference)
class SkipTest(Exception): class SkipTest(Exception):
@ -44,14 +46,15 @@ def skip(reason):
Unconditionally skip a test. Unconditionally skip a test.
""" """
def decorator(test_item): def decorator(test_item):
if isinstance(test_item, type) and issubclass(test_item, TestCase): if not (isinstance(test_item, type) and issubclass(test_item, TestCase)):
test_item.__unittest_skip__ = True @functools.wraps(test_item)
test_item.__unittest_skip_why__ = reason def skip_wrapper(*args, **kwargs):
return test_item raise SkipTest(reason)
@functools.wraps(test_item) test_item = skip_wrapper
def skip_wrapper(*args, **kwargs):
raise SkipTest(reason) test_item.__unittest_skip__ = True
return skip_wrapper test_item.__unittest_skip_why__ = reason
return test_item
return decorator return decorator
def skipIf(condition, reason): def skipIf(condition, reason):
@ -164,6 +167,9 @@ class TestCase(object):
longMessage = False longMessage = False
# Attribute used by TestSuite for classSetUp
_classSetupFailed = False
def __init__(self, methodName='runTest'): def __init__(self, methodName='runTest'):
"""Create an instance of the class that will use the named test """Create an instance of the class that will use the named test
@ -175,7 +181,7 @@ class TestCase(object):
try: try:
testMethod = getattr(self, methodName) testMethod = getattr(self, methodName)
except AttributeError: except AttributeError:
raise ValueError("no such test method in %s: %s" % \ raise ValueError("no such test method in %s: %s" %
(self.__class__, methodName)) (self.__class__, methodName))
self._testMethodDoc = testMethod.__doc__ self._testMethodDoc = testMethod.__doc__
self._cleanups = [] self._cleanups = []
@ -222,6 +228,14 @@ class TestCase(object):
"Hook method for deconstructing the test fixture after testing it." "Hook method for deconstructing the test fixture after testing it."
pass pass
@classmethod
def setUpClass(cls):
"Hook method for setting up class fixture before running tests in the class."
@classmethod
def tearDownClass(cls):
"Hook method for deconstructing the class fixture after running all tests in the class."
def countTestCases(self): def countTestCases(self):
return 1 return 1
@ -240,7 +254,7 @@ class TestCase(object):
def id(self): def id(self):
return "%s.%s" % (util.strclass(self.__class__), self._testMethodName) return "%s.%s" % (strclass(self.__class__), self._testMethodName)
def __eq__(self, other): def __eq__(self, other):
if type(self) is not type(other): if type(self) is not type(other):
@ -255,11 +269,20 @@ class TestCase(object):
return hash((type(self), self._testMethodName)) return hash((type(self), self._testMethodName))
def __str__(self): def __str__(self):
return "%s (%s)" % (self._testMethodName, util.strclass(self.__class__)) return "%s (%s)" % (self._testMethodName, strclass(self.__class__))
def __repr__(self): def __repr__(self):
return "<%s testMethod=%s>" % \ return "<%s testMethod=%s>" % \
(util.strclass(self.__class__), self._testMethodName) (strclass(self.__class__), self._testMethodName)
def _addSkip(self, result, reason):
addSkip = getattr(result, 'addSkip', None)
if addSkip is not None:
addSkip(self, reason)
else:
warnings.warn("TestResult has no addSkip method, skips not reported",
RuntimeWarning, 2)
result.addSuccess(self)
def run(self, result=None): def run(self, result=None):
orig_result = result orig_result = result
@ -271,20 +294,24 @@ class TestCase(object):
self._resultForDoCleanups = result self._resultForDoCleanups = result
result.startTest(self) 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: try:
result.addSkip(self, 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: finally:
result.stopTest(self) result.stopTest(self)
return return
testMethod = getattr(self, self._testMethodName)
try: try:
success = False success = False
try: try:
self.setUp() self.setUp()
except SkipTest as e: except SkipTest as e:
result.addSkip(self, str(e)) self._addSkip(result, str(e))
except Exception: except Exception:
result.addError(self, sys.exc_info()) result.addError(self, sys.exc_info())
else: else:
@ -293,11 +320,23 @@ class TestCase(object):
except self.failureException: except self.failureException:
result.addFailure(self, sys.exc_info()) result.addFailure(self, sys.exc_info())
except _ExpectedFailure as e: except _ExpectedFailure as e:
result.addExpectedFailure(self, e.exc_info) addExpectedFailure = getattr(result, 'addExpectedFailure', None)
if addExpectedFailure is not None:
addExpectedFailure(self, e.exc_info)
else:
warnings.warn("TestResult has no addExpectedFailure method, reporting as passes",
RuntimeWarning)
result.addSuccess(self)
except _UnexpectedSuccess: except _UnexpectedSuccess:
result.addUnexpectedSuccess(self) addUnexpectedSuccess = getattr(result, 'addUnexpectedSuccess', None)
if addUnexpectedSuccess is not None:
addUnexpectedSuccess(self)
else:
warnings.warn("TestResult has no addUnexpectedSuccess method, reporting as failures",
RuntimeWarning)
result.addFailure(self, sys.exc_info())
except SkipTest as e: except SkipTest as e:
result.addSkip(self, str(e)) self._addSkip(result, str(e))
except Exception: except Exception:
result.addError(self, sys.exc_info()) result.addError(self, sys.exc_info())
else: else:
@ -354,13 +393,13 @@ class TestCase(object):
def assertFalse(self, expr, msg=None): def assertFalse(self, expr, msg=None):
"Fail the test if the expression is true." "Fail the test if the expression is true."
if expr: if expr:
msg = self._formatMessage(msg, "%r is not False" % expr) msg = self._formatMessage(msg, "%s is not False" % safe_repr(expr))
raise self.failureException(msg) raise self.failureException(msg)
def assertTrue(self, expr, msg=None): def assertTrue(self, expr, msg=None):
"""Fail the test unless the expression is true.""" """Fail the test unless the expression is true."""
if not expr: if not expr:
msg = self._formatMessage(msg, "%r is not True" % expr) msg = self._formatMessage(msg, "%s is not True" % safe_repr(expr))
raise self.failureException(msg) raise self.failureException(msg)
def _formatMessage(self, msg, standardMsg): def _formatMessage(self, msg, standardMsg):
@ -377,7 +416,12 @@ class TestCase(object):
return msg or standardMsg return msg or standardMsg
if msg is None: if msg is None:
return standardMsg 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): def assertRaises(self, excClass, callableObj=None, *args, **kwargs):
@ -436,7 +480,7 @@ class TestCase(object):
def _baseAssertEqual(self, first, second, msg=None): def _baseAssertEqual(self, first, second, msg=None):
"""The default assertEqual implementation, not type specific.""" """The default assertEqual implementation, not type specific."""
if not first == second: if not first == second:
standardMsg = '%r != %r' % (first, second) standardMsg = '%s != %s' % (safe_repr(first), safe_repr(second))
msg = self._formatMessage(msg, standardMsg) msg = self._formatMessage(msg, standardMsg)
raise self.failureException(msg) raise self.failureException(msg)
@ -452,7 +496,8 @@ class TestCase(object):
operator. operator.
""" """
if not first != second: if not first != second:
msg = self._formatMessage(msg, '%r == %r' % (first, second)) msg = self._formatMessage(msg, '%s == %s' % (safe_repr(first),
safe_repr(second)))
raise self.failureException(msg) raise self.failureException(msg)
def assertAlmostEqual(self, first, second, *, places=7, msg=None): def assertAlmostEqual(self, first, second, *, places=7, msg=None):
@ -467,10 +512,12 @@ class TestCase(object):
compare almost equal. compare almost equal.
""" """
if first == second: if first == second:
# shortcut for ite # shortcut for inf
return return
if round(abs(second-first), places) != 0: if round(abs(second-first), places) != 0:
standardMsg = '%r != %r within %r places' % (first, second, places) standardMsg = '%s != %s within %r places' % (safe_repr(first),
safe_repr(second),
places)
msg = self._formatMessage(msg, standardMsg) msg = self._formatMessage(msg, standardMsg)
raise self.failureException(msg) raise self.failureException(msg)
@ -485,7 +532,9 @@ class TestCase(object):
Objects that are equal automatically fail. Objects that are equal automatically fail.
""" """
if (first == second) or round(abs(second-first), places) == 0: if (first == second) or round(abs(second-first), places) == 0:
standardMsg = '%r == %r within %r places' % (first, second, places) standardMsg = '%s == %s within %r places' % (safe_repr(first),
safe_repr(second),
places)
msg = self._formatMessage(msg, standardMsg) msg = self._formatMessage(msg, standardMsg)
raise self.failureException(msg) raise self.failureException(msg)
@ -535,11 +584,11 @@ class TestCase(object):
if seq_type != None: if seq_type != None:
seq_type_name = seq_type.__name__ seq_type_name = seq_type.__name__
if not isinstance(seq1, seq_type): if not isinstance(seq1, seq_type):
raise self.failureException('First sequence is not a %s: %r' raise self.failureException('First sequence is not a %s: %s'
% (seq_type_name, seq1)) % (seq_type_name, safe_repr(seq1)))
if not isinstance(seq2, seq_type): if not isinstance(seq2, seq_type):
raise self.failureException('Second sequence is not a %s: %r' raise self.failureException('Second sequence is not a %s: %s'
% (seq_type_name, seq2)) % (seq_type_name, safe_repr(seq2)))
else: else:
seq_type_name = "sequence" seq_type_name = "sequence"
@ -561,8 +610,8 @@ class TestCase(object):
if seq1 == seq2: if seq1 == seq2:
return return
seq1_repr = repr(seq1) seq1_repr = safe_repr(seq1)
seq2_repr = repr(seq2) seq2_repr = safe_repr(seq2)
if len(seq1_repr) > 30: if len(seq1_repr) > 30:
seq1_repr = seq1_repr[:30] + '...' seq1_repr = seq1_repr[:30] + '...'
if len(seq2_repr) > 30: if len(seq2_repr) > 30:
@ -689,25 +738,28 @@ class TestCase(object):
def assertIn(self, member, container, msg=None): def assertIn(self, member, container, msg=None):
"""Just like self.assertTrue(a in b), but with a nicer default message.""" """Just like self.assertTrue(a in b), but with a nicer default message."""
if member not in container: if member not in container:
standardMsg = '%r not found in %r' % (member, container) standardMsg = '%s not found in %s' % (safe_repr(member),
safe_repr(container))
self.fail(self._formatMessage(msg, standardMsg)) self.fail(self._formatMessage(msg, standardMsg))
def assertNotIn(self, member, container, msg=None): def assertNotIn(self, member, container, msg=None):
"""Just like self.assertTrue(a not in b), but with a nicer default message.""" """Just like self.assertTrue(a not in b), but with a nicer default message."""
if member in container: if member in container:
standardMsg = '%r unexpectedly found in %r' % (member, container) standardMsg = '%s unexpectedly found in %s' % (safe_repr(member),
safe_repr(container))
self.fail(self._formatMessage(msg, standardMsg)) self.fail(self._formatMessage(msg, standardMsg))
def assertIs(self, expr1, expr2, msg=None): def assertIs(self, expr1, expr2, msg=None):
"""Just like self.assertTrue(a is b), but with a nicer default message.""" """Just like self.assertTrue(a is b), but with a nicer default message."""
if expr1 is not expr2: if expr1 is not expr2:
standardMsg = '%r is not %r' % (expr1, expr2) standardMsg = '%s is not %s' % (safe_repr(expr1),
safe_repr(expr2))
self.fail(self._formatMessage(msg, standardMsg)) self.fail(self._formatMessage(msg, standardMsg))
def assertIsNot(self, expr1, expr2, msg=None): def assertIsNot(self, expr1, expr2, msg=None):
"""Just like self.assertTrue(a is not b), but with a nicer default message.""" """Just like self.assertTrue(a is not b), but with a nicer default message."""
if expr1 is expr2: if expr1 is expr2:
standardMsg = 'unexpectedly identical: %r' % (expr1,) standardMsg = 'unexpectedly identical: %s' % (safe_repr(expr1),)
self.fail(self._formatMessage(msg, standardMsg)) self.fail(self._formatMessage(msg, standardMsg))
def assertDictEqual(self, d1, d2, msg=None): def assertDictEqual(self, d1, d2, msg=None):
@ -729,14 +781,16 @@ class TestCase(object):
missing.append(key) missing.append(key)
elif value != actual[key]: elif value != actual[key]:
mismatched.append('%s, expected: %s, actual: %s' % mismatched.append('%s, expected: %s, actual: %s' %
(key, value, actual[key])) (safe_repr(key), safe_repr(value),
safe_repr(actual[key])))
if not (missing or mismatched): if not (missing or mismatched):
return return
standardMsg = '' standardMsg = ''
if missing: if missing:
standardMsg = 'Missing: %r' % ','.join(missing) standardMsg = 'Missing: %s' % ','.join(safe_repr(m) for m in
missing)
if mismatched: if mismatched:
if standardMsg: if standardMsg:
standardMsg += '; ' standardMsg += '; '
@ -758,10 +812,8 @@ class TestCase(object):
try: try:
expected = set(expected_seq) expected = set(expected_seq)
actual = set(actual_seq) actual = set(actual_seq)
missing = list(expected.difference(actual)) missing = sorted(expected.difference(actual))
unexpected = list(actual.difference(expected)) unexpected = sorted(actual.difference(expected))
missing.sort()
unexpected.sort()
except TypeError: except TypeError:
# Fall back to slower list-compare if any of the objects are # Fall back to slower list-compare if any of the objects are
# not hashable. # not hashable.
@ -771,16 +823,17 @@ class TestCase(object):
expected.sort() expected.sort()
actual.sort() actual.sort()
except TypeError: except TypeError:
missing, unexpected = util.unorderable_list_difference(expected, missing, unexpected = unorderable_list_difference(expected,
actual)
else:
missing, unexpected = util.sorted_list_difference(expected,
actual) actual)
else:
missing, unexpected = sorted_list_difference(expected, actual)
errors = [] errors = []
if missing: if missing:
errors.append('Expected, but missing:\n %r' % missing) errors.append('Expected, but missing:\n %s' %
safe_repr(missing))
if unexpected: if unexpected:
errors.append('Unexpected, but present:\n %r' % unexpected) errors.append('Unexpected, but present:\n %s' %
safe_repr(unexpected))
if errors: if errors:
standardMsg = '\n'.join(errors) standardMsg = '\n'.join(errors)
self.fail(self._formatMessage(msg, standardMsg)) self.fail(self._formatMessage(msg, standardMsg))
@ -800,31 +853,31 @@ class TestCase(object):
def assertLess(self, a, b, msg=None): def assertLess(self, a, b, msg=None):
"""Just like self.assertTrue(a < b), but with a nicer default message.""" """Just like self.assertTrue(a < b), but with a nicer default message."""
if not a < b: if not a < b:
standardMsg = '%r not less than %r' % (a, b) standardMsg = '%s not less than %s' % (safe_repr(a), safe_repr(b))
self.fail(self._formatMessage(msg, standardMsg)) self.fail(self._formatMessage(msg, standardMsg))
def assertLessEqual(self, a, b, msg=None): def assertLessEqual(self, a, b, msg=None):
"""Just like self.assertTrue(a <= b), but with a nicer default message.""" """Just like self.assertTrue(a <= b), but with a nicer default message."""
if not a <= b: if not a <= b:
standardMsg = '%r not less than or equal to %r' % (a, b) standardMsg = '%s not less than or equal to %s' % (safe_repr(a), safe_repr(b))
self.fail(self._formatMessage(msg, standardMsg)) self.fail(self._formatMessage(msg, standardMsg))
def assertGreater(self, a, b, msg=None): def assertGreater(self, a, b, msg=None):
"""Just like self.assertTrue(a > b), but with a nicer default message.""" """Just like self.assertTrue(a > b), but with a nicer default message."""
if not a > b: if not a > b:
standardMsg = '%r not greater than %r' % (a, b) standardMsg = '%s not greater than %s' % (safe_repr(a), safe_repr(b))
self.fail(self._formatMessage(msg, standardMsg)) self.fail(self._formatMessage(msg, standardMsg))
def assertGreaterEqual(self, a, b, msg=None): def assertGreaterEqual(self, a, b, msg=None):
"""Just like self.assertTrue(a >= b), but with a nicer default message.""" """Just like self.assertTrue(a >= b), but with a nicer default message."""
if not a >= b: if not a >= b:
standardMsg = '%r not greater than or equal to %r' % (a, b) standardMsg = '%s not greater than or equal to %s' % (safe_repr(a), safe_repr(b))
self.fail(self._formatMessage(msg, standardMsg)) self.fail(self._formatMessage(msg, standardMsg))
def assertIsNone(self, obj, msg=None): def assertIsNone(self, obj, msg=None):
"""Same as self.assertTrue(obj is None), with a nicer default message.""" """Same as self.assertTrue(obj is None), with a nicer default message."""
if obj is not None: if obj is not None:
standardMsg = '%r is not None' % obj standardMsg = '%s is not None' % (safe_repr(obj),)
self.fail(self._formatMessage(msg, standardMsg)) self.fail(self._formatMessage(msg, standardMsg))
def assertIsNotNone(self, obj, msg=None): def assertIsNotNone(self, obj, msg=None):
@ -837,13 +890,13 @@ class TestCase(object):
"""Same as self.assertTrue(isinstance(obj, cls)), with a nicer """Same as self.assertTrue(isinstance(obj, cls)), with a nicer
default message.""" default message."""
if not isinstance(obj, cls): if not isinstance(obj, cls):
standardMsg = '%r is not an instance of %r' % (obj, cls) standardMsg = '%s is not an instance of %r' % (safe_repr(obj), cls)
self.fail(self._formatMessage(msg, standardMsg)) self.fail(self._formatMessage(msg, standardMsg))
def assertNotIsInstance(self, obj, cls, msg=None): def assertNotIsInstance(self, obj, cls, msg=None):
"""Included for symmetry with assertIsInstance.""" """Included for symmetry with assertIsInstance."""
if isinstance(obj, cls): if isinstance(obj, cls):
standardMsg = '%r is an instance of %r' % (obj, cls) standardMsg = '%s is an instance of %r' % (safe_repr(obj), cls)
self.fail(self._formatMessage(msg, standardMsg)) self.fail(self._formatMessage(msg, standardMsg))
def assertRaisesRegexp(self, expected_exception, expected_regexp, def assertRaisesRegexp(self, expected_exception, expected_regexp,
@ -921,11 +974,11 @@ class FunctionTestCase(TestCase):
self._testFunc, self._description)) self._testFunc, self._description))
def __str__(self): def __str__(self):
return "%s (%s)" % (util.strclass(self.__class__), return "%s (%s)" % (strclass(self.__class__),
self._testFunc.__name__) self._testFunc.__name__)
def __repr__(self): def __repr__(self):
return "<%s testFunc=%s>" % (util.strclass(self.__class__), return "<%s tec=%s>" % (strclass(self.__class__),
self._testFunc) self._testFunc)
def shortDescription(self): def shortDescription(self):

View File

@ -16,7 +16,9 @@ class TestResult(object):
contain tuples of (testcase, exceptioninfo), where exceptioninfo is the contain tuples of (testcase, exceptioninfo), where exceptioninfo is the
formatted traceback of the error that occurred. formatted traceback of the error that occurred.
""" """
def __init__(self): _previousTestClass = None
_moduleSetUpFailed = False
def __init__(self, stream=None, descriptions=None, verbosity=None):
self.failures = [] self.failures = []
self.errors = [] self.errors = []
self.testsRun = 0 self.testsRun = 0
@ -25,6 +27,9 @@ class TestResult(object):
self.unexpectedSuccesses = [] self.unexpectedSuccesses = []
self.shouldStop = False self.shouldStop = False
def printErrors(self):
"Called by TestRunner after test run"
def startTest(self, test): def startTest(self, test):
"Called when the given test is about to be run" "Called when the given test is about to be run"
self.testsRun += 1 self.testsRun += 1
@ -107,6 +112,6 @@ class TestResult(object):
return length return length
def __repr__(self): def __repr__(self):
return "<%s run=%i errors=%i failures=%i>" % \ return ("<%s run=%i errors=%i failures=%i>" %
(util.strclass(self.__class__), self.testsRun, len(self.errors), (util.strclass(self.__class__), self.testsRun, len(self.errors),
len(self.failures)) len(self.failures)))

View File

@ -148,15 +148,22 @@ class TextTestRunner(object):
stopTime = time.time() stopTime = time.time()
timeTaken = stopTime - startTime timeTaken = stopTime - startTime
result.printErrors() result.printErrors()
self.stream.writeln(result.separator2) if hasattr(result, 'separator2'):
self.stream.writeln(result.separator2)
run = result.testsRun run = result.testsRun
self.stream.writeln("Ran %d test%s in %.3fs" % self.stream.writeln("Ran %d test%s in %.3fs" %
(run, run != 1 and "s" or "", timeTaken)) (run, run != 1 and "s" or "", timeTaken))
self.stream.writeln() self.stream.writeln()
results = map(len, (result.expectedFailures,
result.unexpectedSuccesses, expectedFails = unexpectedSuccesses = skipped = 0
result.skipped)) try:
expectedFails, unexpectedSuccesses, skipped = results results = map(len, (result.expectedFailures,
result.unexpectedSuccesses,
result.skipped))
expectedFails, unexpectedSuccesses, skipped = results
except AttributeError:
pass
infos = [] infos = []
if not result.wasSuccessful(): if not result.wasSuccessful():
self.stream.write("FAILED") self.stream.write("FAILED")

View File

@ -1,17 +1,13 @@
"""TestSuite""" """TestSuite"""
import sys
from . import case from . import case
from . import util from . import util
class TestSuite(object): class BaseTestSuite(object):
"""A test suite is a composite test consisting of a number of TestCases. """A simple test suite that doesn't provide class or module shared fixtures.
For use, create an instance of TestSuite, then add test case instances.
When all tests have been added, the suite can be passed to a test
runner, such as TextTestRunner. It will run the individual test cases
in the order in which they were added, aggregating the results. When
subclassing, do not forget to call the base class constructor.
""" """
def __init__(self, tests=()): def __init__(self, tests=()):
self._tests = [] self._tests = []
@ -67,3 +63,190 @@ class TestSuite(object):
"""Run the tests without collecting errors in a TestResult""" """Run the tests without collecting errors in a TestResult"""
for test in self: for test in self:
test.debug() test.debug()
class TestSuite(BaseTestSuite):
"""A test suite is a composite test consisting of a number of TestCases.
For use, create an instance of TestSuite, then add test case instances.
When all tests have been added, the suite can be passed to a test
runner, such as TextTestRunner. It will run the individual test cases
in the order in which they were added, aggregating the results. When
subclassing, do not forget to call the base class constructor.
"""
def run(self, result):
self._wrapped_run(result)
self._tearDownPreviousClass(None, result)
self._handleModuleTearDown(result)
return result
################################
# private methods
def _wrapped_run(self, result):
for test in self:
if result.shouldStop:
break
if _isnotsuite(test):
self._tearDownPreviousClass(test, result)
self._handleModuleFixture(test, result)
self._handleClassSetUp(test, result)
result._previousTestClass = test.__class__
if (getattr(test.__class__, '_classSetupFailed', False) or
getattr(result, '_moduleSetUpFailed', False)):
continue
if hasattr(test, '_wrapped_run'):
test._wrapped_run(result)
else:
test(result)
def _handleClassSetUp(self, test, result):
previousClass = getattr(result, '_previousTestClass', None)
currentClass = test.__class__
if currentClass == previousClass:
return
if result._moduleSetUpFailed:
return
if getattr(currentClass, "__unittest_skip__", False):
return
currentClass._classSetupFailed = False
setUpClass = getattr(currentClass, 'setUpClass', None)
if setUpClass is not None:
try:
setUpClass()
except:
currentClass._classSetupFailed = True
self._addClassSetUpError(result, currentClass)
def _get_previous_module(self, result):
previousModule = None
previousClass = getattr(result, '_previousTestClass', None)
if previousClass is not None:
previousModule = previousClass.__module__
return previousModule
def _handleModuleFixture(self, test, result):
previousModule = self._get_previous_module(result)
currentModule = test.__class__.__module__
if currentModule == previousModule:
return
self._handleModuleTearDown(result)
result._moduleSetUpFailed = False
try:
module = sys.modules[currentModule]
except KeyError:
return
setUpModule = getattr(module, 'setUpModule', None)
if setUpModule is not None:
try:
setUpModule()
except:
result._moduleSetUpFailed = True
error = _ErrorHolder('setUpModule (%s)' % currentModule)
result.addError(error, sys.exc_info())
def _handleModuleTearDown(self, result):
previousModule = self._get_previous_module(result)
if previousModule is None:
return
if result._moduleSetUpFailed:
return
try:
module = sys.modules[previousModule]
except KeyError:
return
tearDownModule = getattr(module, 'tearDownModule', None)
if tearDownModule is not None:
try:
tearDownModule()
except:
error = _ErrorHolder('tearDownModule (%s)' % previousModule)
result.addError(error, sys.exc_info())
def _tearDownPreviousClass(self, test, result):
previousClass = getattr(result, '_previousTestClass', None)
currentClass = test.__class__
if currentClass == previousClass:
return
if getattr(previousClass, '_classSetupFailed', False):
return
if getattr(result, '_moduleSetUpFailed', False):
return
if getattr(previousClass, "__unittest_skip__", False):
return
tearDownClass = getattr(previousClass, 'tearDownClass', None)
if tearDownClass is not None:
try:
tearDownClass()
except:
self._addClassTearDownError(result)
def _addClassTearDownError(self, result):
className = util.strclass(result._previousTestClass)
error = _ErrorHolder('classTearDown (%s)' % className)
result.addError(error, sys.exc_info())
def _addClassSetUpError(self, result, klass):
className = util.strclass(klass)
error = _ErrorHolder('classSetUp (%s)' % className)
result.addError(error, sys.exc_info())
class _ErrorHolder(object):
"""
Placeholder for a TestCase inside a result. As far as a TestResult
is concerned, this looks exactly like a unit test. Used to insert
arbitrary errors into a test suite run.
"""
# Inspired by the ErrorHolder from Twisted:
# http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
# attribute used by TestResult._exc_info_to_string
failureException = None
def __init__(self, description):
self.description = description
def id(self):
return self.description
def shortDescription(self):
return None
def __repr__(self):
return "<ErrorHolder description=%r>" % (self.description,)
def __str__(self):
return self.id()
def run(self, result):
# could call result.addError(...) - but this test-like object
# shouldn't be run anyway
pass
def __call__(self, result):
return self.run(result)
def countTestCases(self):
return 0
def _isnotsuite(test):
"A crude way to tell apart testcases and suites with duck-typing"
try:
iter(test)
except TypeError:
return True
return False

View File

@ -1,5 +1,11 @@
"""Various utility functions.""" """Various utility functions."""
def safe_repr(obj):
try:
return repr(obj)
except Exception:
return object.__repr__(obj)
def strclass(cls): def strclass(cls):
return "%s.%s" % (cls.__module__, cls.__name__) return "%s.%s" % (cls.__module__, cls.__name__)