diff options
Diffstat (limited to 'Lib/test/test_unittest.py')
-rw-r--r-- | Lib/test/test_unittest.py | 413 |
1 files changed, 374 insertions, 39 deletions
diff --git a/Lib/test/test_unittest.py b/Lib/test/test_unittest.py index d0d2049060..e7097cc71c 100644 --- a/Lib/test/test_unittest.py +++ b/Lib/test/test_unittest.py @@ -9,9 +9,10 @@ Still need testing: import re from test import support import unittest -from unittest import TestCase +from unittest import TestCase, TestProgram import types from copy import deepcopy +import io ### Support code ################################################################ @@ -25,10 +26,18 @@ class LoggingResult(unittest.TestResult): self._events.append('startTest') super().startTest(test) + def startTestRun(self): + self._events.append('startTestRun') + super(LoggingResult, self).startTestRun() + def stopTest(self, test): self._events.append('stopTest') super().stopTest(test) + def stopTestRun(self): + self._events.append('stopTestRun') + super(LoggingResult, self).stopTestRun() + def addFailure(self, *args): self._events.append('addFailure') super().addFailure(*args) @@ -1826,6 +1835,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" @@ -1973,6 +1988,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 @@ -2067,19 +2129,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 @@ -2093,20 +2166,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 @@ -2120,20 +2205,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 @@ -2147,22 +2242,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 @@ -2253,7 +2370,9 @@ class Test_TestCase(TestCase, TestEquality, TestHashing): self.failUnless(isinstance(Foo().id(), str)) # "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 = [] @@ -2267,7 +2386,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): @@ -3012,6 +3132,220 @@ class TestLongMessage(TestCase): "^unexpectedly identical: None : oops$"]) +class TestCleanUp(TestCase): + + def testCleanUp(self): + class TestableTest(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(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._result = 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(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 = unittest.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']) + + +class Test_TestProgram(TestCase): + + # Horrible white box test + def testNoExit(self): + result = object() + test = object() + + class FakeRunner(object): + def run(self, test): + self.test = test + return result + + runner = FakeRunner() + + try: + oldParseArgs = TestProgram.parseArgs + TestProgram.parseArgs = lambda *args: None + TestProgram.test = test + + program = TestProgram(testRunner=runner, exit=False) + + self.assertEqual(program.result, result) + self.assertEqual(runner.test, test) + + finally: + TestProgram.parseArgs = oldParseArgs + del TestProgram.test + + + class FooBar(unittest.TestCase): + def testPass(self): + assert True + def testFail(self): + assert False + + class FooBarLoader(unittest.TestLoader): + """Test loader that returns a suite containing FooBar.""" + def loadTestsFromModule(self, module): + return self.suiteClass( + [self.loadTestsFromTestCase(Test_TestProgram.FooBar)]) + + + def test_NonExit(self): + program = unittest.main(exit=False, + argv=["foobar"], + testRunner=unittest.TextTestRunner(stream=io.StringIO()), + testLoader=self.FooBarLoader()) + self.assertTrue(hasattr(program, 'result')) + + + def test_Exit(self): + self.assertRaises( + SystemExit, + unittest.main, + argv=["foobar"], + testRunner=unittest.TextTestRunner(stream=io.StringIO()), + exit=True, + testLoader=self.FooBarLoader()) + + + def test_ExitAsDefault(self): + self.assertRaises( + SystemExit, + unittest.main, + argv=["foobar"], + testRunner=unittest.TextTestRunner(stream=io.StringIO()), + 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__(io.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__(io.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 ###################################################################### @@ -3019,7 +3353,8 @@ class TestLongMessage(TestCase): def test_main(): support.run_unittest(Test_TestCase, Test_TestLoader, Test_TestSuite, Test_TestResult, Test_FunctionTestCase, - Test_TestSkipping, Test_Assertions, TestLongMessage) + Test_TestSkipping, Test_Assertions, TestLongMessage, + Test_TestProgram, TestCleanUp) if __name__ == "__main__": test_main() |