summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorMichael Foord <fuzzyman@voidspace.org.uk>2009-05-02 22:43:34 (GMT)
committerMichael Foord <fuzzyman@voidspace.org.uk>2009-05-02 22:43:34 (GMT)
commit07ef487a96b826584f7871629c4cc4754e41242b (patch)
tree8c41c3adc7021d39841b87ffaf44afecc50794b1 /Lib
parent7430989cdadfb5aacef6909a3e2c033a0209699b (diff)
downloadcpython-07ef487a96b826584f7871629c4cc4754e41242b.zip
cpython-07ef487a96b826584f7871629c4cc4754e41242b.tar.gz
cpython-07ef487a96b826584f7871629c4cc4754e41242b.tar.bz2
Diffstat (limited to 'Lib')
-rw-r--r--Lib/test/test_unittest.py234
-rw-r--r--Lib/unittest.py31
2 files changed, 227 insertions, 38 deletions
diff --git a/Lib/test/test_unittest.py b/Lib/test/test_unittest.py
index 13bd78a..c77cc16 100644
--- a/Lib/test/test_unittest.py
+++ b/Lib/test/test_unittest.py
@@ -6,6 +6,7 @@ Still need testing:
TestCase.{assert,fail}* methods (some are tested implicitly)
"""
+from StringIO import StringIO
import re
from test import test_support
import unittest
@@ -26,10 +27,18 @@ class LoggingResult(unittest.TestResult):
self._events.append('startTest')
super(LoggingResult, self).startTest(test)
+ def startTestRun(self):
+ self._events.append('startTestRun')
+ super(LoggingResult, self).startTestRun()
+
def stopTest(self, test):
self._events.append('stopTest')
super(LoggingResult, self).stopTest(test)
+ def stopTestRun(self):
+ self._events.append('stopTestRun')
+ super(LoggingResult, self).stopTestRun()
+
def addFailure(self, *args):
self._events.append('addFailure')
super(LoggingResult, self).addFailure(*args)
@@ -1817,6 +1826,12 @@ class Test_TestResult(TestCase):
self.assertEqual(result.testsRun, 1)
self.assertEqual(result.shouldStop, False)
+ # "Called before and after tests are run. The default implementation does nothing."
+ def test_startTestRun_stopTestRun(self):
+ result = unittest.TestResult()
+ result.startTestRun()
+ result.stopTestRun()
+
# "addSuccess(test)"
# ...
# "Called when the test case test succeeds"
@@ -1964,6 +1979,53 @@ class Foo(unittest.TestCase):
class Bar(Foo):
def test2(self): pass
+class LoggingTestCase(unittest.TestCase):
+ """A test case which logs its calls."""
+
+ def __init__(self, events):
+ super(LoggingTestCase, self).__init__('test')
+ self.events = events
+
+ def setUp(self):
+ self.events.append('setUp')
+
+ def test(self):
+ self.events.append('test')
+
+ def tearDown(self):
+ self.events.append('tearDown')
+
+class ResultWithNoStartTestRunStopTestRun(object):
+ """An object honouring TestResult before startTestRun/stopTestRun."""
+
+ def __init__(self):
+ self.failures = []
+ self.errors = []
+ self.testsRun = 0
+ self.skipped = []
+ self.expectedFailures = []
+ self.unexpectedSuccesses = []
+ self.shouldStop = False
+
+ def startTest(self, test):
+ pass
+
+ def stopTest(self, test):
+ pass
+
+ def addError(self, test):
+ pass
+
+ def addFailure(self, test):
+ pass
+
+ def addSuccess(self, test):
+ pass
+
+ def wasSuccessful(self):
+ return True
+
+
################################################################
### /Support code for Test_TestCase
@@ -2058,19 +2120,30 @@ class Test_TestCase(TestCase, TestEquality, TestHashing):
events = []
result = LoggingResult(events)
- class Foo(unittest.TestCase):
+ class Foo(LoggingTestCase):
def setUp(self):
- events.append('setUp')
+ super(Foo, self).setUp()
raise RuntimeError('raised by Foo.setUp')
- def test(self):
- events.append('test')
+ Foo(events).run(result)
+ expected = ['startTest', 'setUp', 'addError', 'stopTest']
+ self.assertEqual(events, expected)
- def tearDown(self):
- events.append('tearDown')
+ # "With a temporary result stopTestRun is called when setUp errors.
+ def test_run_call_order__error_in_setUp_default_result(self):
+ events = []
- Foo('test').run(result)
- expected = ['startTest', 'setUp', 'addError', 'stopTest']
+ class Foo(LoggingTestCase):
+ def defaultTestResult(self):
+ return LoggingResult(self.events)
+
+ def setUp(self):
+ super(Foo, self).setUp()
+ raise RuntimeError('raised by Foo.setUp')
+
+ Foo(events).run()
+ expected = ['startTestRun', 'startTest', 'setUp', 'addError',
+ 'stopTest', 'stopTestRun']
self.assertEqual(events, expected)
# "When a setUp() method is defined, the test runner will run that method
@@ -2084,20 +2157,32 @@ class Test_TestCase(TestCase, TestEquality, TestHashing):
events = []
result = LoggingResult(events)
- class Foo(unittest.TestCase):
- def setUp(self):
- events.append('setUp')
-
+ class Foo(LoggingTestCase):
def test(self):
- events.append('test')
+ super(Foo, self).test()
raise RuntimeError('raised by Foo.test')
- def tearDown(self):
- events.append('tearDown')
-
expected = ['startTest', 'setUp', 'test', 'addError', 'tearDown',
'stopTest']
- Foo('test').run(result)
+ Foo(events).run(result)
+ self.assertEqual(events, expected)
+
+ # "With a default result, an error in the test still results in stopTestRun
+ # being called."
+ def test_run_call_order__error_in_test_default_result(self):
+ events = []
+
+ class Foo(LoggingTestCase):
+ def defaultTestResult(self):
+ return LoggingResult(self.events)
+
+ def test(self):
+ super(Foo, self).test()
+ raise RuntimeError('raised by Foo.test')
+
+ expected = ['startTestRun', 'startTest', 'setUp', 'test', 'addError',
+ 'tearDown', 'stopTest', 'stopTestRun']
+ Foo(events).run()
self.assertEqual(events, expected)
# "When a setUp() method is defined, the test runner will run that method
@@ -2111,20 +2196,30 @@ class Test_TestCase(TestCase, TestEquality, TestHashing):
events = []
result = LoggingResult(events)
- class Foo(unittest.TestCase):
- def setUp(self):
- events.append('setUp')
-
+ class Foo(LoggingTestCase):
def test(self):
- events.append('test')
+ super(Foo, self).test()
self.fail('raised by Foo.test')
- def tearDown(self):
- events.append('tearDown')
-
expected = ['startTest', 'setUp', 'test', 'addFailure', 'tearDown',
'stopTest']
- Foo('test').run(result)
+ Foo(events).run(result)
+ self.assertEqual(events, expected)
+
+ # "When a test fails with a default result stopTestRun is still called."
+ def test_run_call_order__failure_in_test_default_result(self):
+
+ class Foo(LoggingTestCase):
+ def defaultTestResult(self):
+ return LoggingResult(self.events)
+ def test(self):
+ super(Foo, self).test()
+ self.fail('raised by Foo.test')
+
+ expected = ['startTestRun', 'startTest', 'setUp', 'test', 'addFailure',
+ 'tearDown', 'stopTest', 'stopTestRun']
+ events = []
+ Foo(events).run()
self.assertEqual(events, expected)
# "When a setUp() method is defined, the test runner will run that method
@@ -2138,22 +2233,44 @@ class Test_TestCase(TestCase, TestEquality, TestHashing):
events = []
result = LoggingResult(events)
- class Foo(unittest.TestCase):
- def setUp(self):
- events.append('setUp')
-
- def test(self):
- events.append('test')
-
+ class Foo(LoggingTestCase):
def tearDown(self):
- events.append('tearDown')
+ super(Foo, self).tearDown()
raise RuntimeError('raised by Foo.tearDown')
- Foo('test').run(result)
+ Foo(events).run(result)
expected = ['startTest', 'setUp', 'test', 'tearDown', 'addError',
'stopTest']
self.assertEqual(events, expected)
+ # "When tearDown errors with a default result stopTestRun is still called."
+ def test_run_call_order__error_in_tearDown_default_result(self):
+
+ class Foo(LoggingTestCase):
+ def defaultTestResult(self):
+ return LoggingResult(self.events)
+ def tearDown(self):
+ super(Foo, self).tearDown()
+ raise RuntimeError('raised by Foo.tearDown')
+
+ events = []
+ Foo(events).run()
+ expected = ['startTestRun', 'startTest', 'setUp', 'test', 'tearDown',
+ 'addError', 'stopTest', 'stopTestRun']
+ self.assertEqual(events, expected)
+
+ # "TestCase.run() still works when the defaultTestResult is a TestResult
+ # that does not support startTestRun and stopTestRun.
+ def test_run_call_order_default_result(self):
+
+ class Foo(unittest.TestCase):
+ def defaultTestResult(self):
+ return ResultWithNoStartTestRunStopTestRun()
+ def test(self):
+ pass
+
+ Foo('test').run()
+
# "This class attribute gives the exception raised by the test() method.
# If a test framework needs to use a specialized exception, possibly to
# carry additional information, it must subclass this exception in
@@ -2244,7 +2361,9 @@ class Test_TestCase(TestCase, TestEquality, TestHashing):
self.failUnless(isinstance(Foo().id(), basestring))
# "If result is omitted or None, a temporary result object is created
- # and used, but is not made available to the caller"
+ # and used, but is not made available to the caller. As TestCase owns the
+ # temporary result startTestRun and stopTestRun are called.
+
def test_run__uses_defaultTestResult(self):
events = []
@@ -2258,7 +2377,8 @@ class Test_TestCase(TestCase, TestEquality, TestHashing):
# Make run() find a result object on its own
Foo('test').run()
- expected = ['startTest', 'test', 'addSuccess', 'stopTest']
+ expected = ['startTestRun', 'startTest', 'test', 'addSuccess',
+ 'stopTest', 'stopTestRun']
self.assertEqual(events, expected)
def testShortDescriptionWithoutDocstring(self):
@@ -3215,6 +3335,46 @@ class Test_TestProgram(TestCase):
testLoader=self.FooBarLoader())
+class Test_TextTestRunner(TestCase):
+ """Tests for TextTestRunner."""
+
+ def test_works_with_result_without_startTestRun_stopTestRun(self):
+ class OldTextResult(ResultWithNoStartTestRunStopTestRun):
+ separator2 = ''
+ def printErrors(self):
+ pass
+
+ class Runner(unittest.TextTestRunner):
+ def __init__(self):
+ super(Runner, self).__init__(StringIO())
+
+ def _makeResult(self):
+ return OldTextResult()
+
+ runner = Runner()
+ runner.run(unittest.TestSuite())
+
+ def test_startTestRun_stopTestRun_called(self):
+ class LoggingTextResult(LoggingResult):
+ separator2 = ''
+ def printErrors(self):
+ pass
+
+ class LoggingRunner(unittest.TextTestRunner):
+ def __init__(self, events):
+ super(LoggingRunner, self).__init__(StringIO())
+ self._events = events
+
+ def _makeResult(self):
+ return LoggingTextResult(self._events)
+
+ events = []
+ runner = LoggingRunner(events)
+ runner.run(unittest.TestSuite())
+ expected = ['startTestRun', 'stopTestRun']
+ self.assertEqual(events, expected)
+
+
######################################################################
## Main
######################################################################
diff --git a/Lib/unittest.py b/Lib/unittest.py
index d5c2927..b2dc320 100644
--- a/Lib/unittest.py
+++ b/Lib/unittest.py
@@ -186,10 +186,22 @@ class TestResult(object):
"Called when the given test is about to be run"
self.testsRun = self.testsRun + 1
+ def startTestRun(self):
+ """Called once before any tests are executed.
+
+ See startTest for a method called before each test.
+ """
+
def stopTest(self, test):
"Called when the given test has been run"
pass
+ def stopTestRun(self):
+ """Called once after all tests are executed.
+
+ See stopTest for a method called after each test.
+ """
+
def addError(self, test, err):
"""Called when an error has occurred. 'err' is a tuple of values as
returned by sys.exc_info().
@@ -437,8 +449,13 @@ class TestCase(object):
(_strclass(self.__class__), self._testMethodName)
def run(self, result=None):
+ orig_result = result
if result is None:
result = self.defaultTestResult()
+ startTestRun = getattr(result, 'startTestRun', None)
+ if startTestRun is not None:
+ startTestRun()
+
self._result = result
result.startTest(self)
testMethod = getattr(self, self._testMethodName)
@@ -478,6 +495,10 @@ class TestCase(object):
result.addSuccess(self)
finally:
result.stopTest(self)
+ if orig_result is None:
+ stopTestRun = getattr(result, 'stopTestRun', None)
+ if stopTestRun is not None:
+ stopTestRun()
def doCleanups(self):
"""Execute all cleanup functions. Normally called for you after
@@ -1433,7 +1454,15 @@ class TextTestRunner(object):
"Run the given test case or test suite."
result = self._makeResult()
startTime = time.time()
- test(result)
+ startTestRun = getattr(result, 'startTestRun', None)
+ if startTestRun is not None:
+ startTestRun()
+ try:
+ test(result)
+ finally:
+ stopTestRun = getattr(result, 'stopTestRun', None)
+ if stopTestRun is not None:
+ stopTestRun()
stopTime = time.time()
timeTaken = stopTime - startTime
result.printErrors()