0
0
mirror of https://github.com/python/cpython.git synced 2024-11-24 17:47:13 +01:00
cpython/Lib/test/test_unittest/test_runner.py
Nikita Sobolev fd9d70a94d
gh-106300: Improve errors testing in test_unittest.test_runner (GH-106737)
Use a custom exception to prevent unintentional silence of actual errors.
2023-08-16 12:20:42 +03:00

1454 lines
51 KiB
Python

import io
import os
import sys
import pickle
import subprocess
from test import support
import unittest
from unittest.case import _Outcome
from test.test_unittest.support import (
BufferedWriter,
LoggingResult,
ResultWithNoStartTestRunStopTestRun,
)
def resultFactory(*_):
return unittest.TestResult()
def getRunner():
return unittest.TextTestRunner(resultclass=resultFactory,
stream=io.StringIO())
class CustomError(Exception):
pass
# For test output compat:
CustomErrorRepr = f"{__name__ + '.' if __name__ != '__main__' else ''}CustomError"
def runTests(*cases):
suite = unittest.TestSuite()
for case in cases:
tests = unittest.defaultTestLoader.loadTestsFromTestCase(case)
suite.addTests(tests)
runner = 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 cleanup(ordering, blowUp=False):
if not blowUp:
ordering.append('cleanup_good')
else:
ordering.append('cleanup_exc')
raise CustomError('CleanUpExc')
class TestCM:
def __init__(self, ordering, enter_result=None):
self.ordering = ordering
self.enter_result = enter_result
def __enter__(self):
self.ordering.append('enter')
return self.enter_result
def __exit__(self, *exc_info):
self.ordering.append('exit')
class LacksEnterAndExit:
pass
class LacksEnter:
def __exit__(self, *exc_info):
pass
class LacksExit:
def __enter__(self):
pass
class TestCleanUp(unittest.TestCase):
def testCleanUp(self):
class TestableTest(unittest.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, (), {})])
self.assertTrue(test.doCleanups())
self.assertEqual(cleanups, [(2, (), {}), (1, (1, 2, 3), dict(four='hello', five='goodbye'))])
def testCleanUpWithErrors(self):
class TestableTest(unittest.TestCase):
def testNothing(self):
pass
test = TestableTest('testNothing')
result = unittest.TestResult()
outcome = test._outcome = _Outcome(result=result)
CleanUpExc = CustomError('foo')
exc2 = CustomError('bar')
def cleanup1():
raise CleanUpExc
def cleanup2():
raise exc2
test.addCleanup(cleanup1)
test.addCleanup(cleanup2)
self.assertFalse(test.doCleanups())
self.assertFalse(outcome.success)
(_, msg2), (_, msg1) = result.errors
self.assertIn('in cleanup1', msg1)
self.assertIn('raise CleanUpExc', msg1)
self.assertIn(f'{CustomErrorRepr}: foo', msg1)
self.assertIn('in cleanup2', msg2)
self.assertIn('raise exc2', msg2)
self.assertIn(f'{CustomErrorRepr}: bar', msg2)
def testCleanupInRun(self):
blowUp = False
ordering = []
class TestableTest(unittest.TestCase):
def setUp(self):
ordering.append('setUp')
test.addCleanup(cleanup2)
if blowUp:
raise CustomError('foo')
def testNothing(self):
ordering.append('test')
test.addCleanup(cleanup3)
def tearDown(self):
ordering.append('tearDown')
test = TestableTest('testNothing')
def cleanup1():
ordering.append('cleanup1')
def cleanup2():
ordering.append('cleanup2')
def cleanup3():
ordering.append('cleanup3')
test.addCleanup(cleanup1)
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', 'cleanup3',
'cleanup2', 'cleanup1', 'success'])
blowUp = True
ordering = []
test = TestableTest('testNothing')
test.addCleanup(cleanup1)
test.run(result)
self.assertEqual(ordering, ['setUp', 'cleanup2', 'cleanup1'])
def testTestCaseDebugExecutesCleanups(self):
ordering = []
class TestableTest(unittest.TestCase):
def setUp(self):
ordering.append('setUp')
self.addCleanup(cleanup1)
def testNothing(self):
ordering.append('test')
self.addCleanup(cleanup3)
def tearDown(self):
ordering.append('tearDown')
test.addCleanup(cleanup4)
test = TestableTest('testNothing')
def cleanup1():
ordering.append('cleanup1')
test.addCleanup(cleanup2)
def cleanup2():
ordering.append('cleanup2')
def cleanup3():
ordering.append('cleanup3')
def cleanup4():
ordering.append('cleanup4')
test.debug()
self.assertEqual(ordering, ['setUp', 'test', 'tearDown', 'cleanup4',
'cleanup3', 'cleanup1', 'cleanup2'])
def test_enterContext(self):
class TestableTest(unittest.TestCase):
def testNothing(self):
pass
test = TestableTest('testNothing')
cleanups = []
test.addCleanup(cleanups.append, 'cleanup1')
cm = TestCM(cleanups, 42)
self.assertEqual(test.enterContext(cm), 42)
test.addCleanup(cleanups.append, 'cleanup2')
self.assertTrue(test.doCleanups())
self.assertEqual(cleanups, ['enter', 'cleanup2', 'exit', 'cleanup1'])
def test_enterContext_arg_errors(self):
class TestableTest(unittest.TestCase):
def testNothing(self):
pass
test = TestableTest('testNothing')
with self.assertRaisesRegex(TypeError, 'the context manager'):
test.enterContext(LacksEnterAndExit())
with self.assertRaisesRegex(TypeError, 'the context manager'):
test.enterContext(LacksEnter())
with self.assertRaisesRegex(TypeError, 'the context manager'):
test.enterContext(LacksExit())
self.assertEqual(test._cleanups, [])
class TestClassCleanup(unittest.TestCase):
def test_addClassCleanUp(self):
class TestableTest(unittest.TestCase):
def testNothing(self):
pass
test = TestableTest('testNothing')
self.assertEqual(test._class_cleanups, [])
class_cleanups = []
def class_cleanup1(*args, **kwargs):
class_cleanups.append((3, args, kwargs))
def class_cleanup2(*args, **kwargs):
class_cleanups.append((4, args, kwargs))
TestableTest.addClassCleanup(class_cleanup1, 1, 2, 3,
four='hello', five='goodbye')
TestableTest.addClassCleanup(class_cleanup2)
self.assertEqual(test._class_cleanups,
[(class_cleanup1, (1, 2, 3),
dict(four='hello', five='goodbye')),
(class_cleanup2, (), {})])
TestableTest.doClassCleanups()
self.assertEqual(class_cleanups, [(4, (), {}), (3, (1, 2, 3),
dict(four='hello', five='goodbye'))])
def test_run_class_cleanUp(self):
ordering = []
blowUp = True
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering)
if blowUp:
raise CustomError()
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
runTests(TestableTest)
self.assertEqual(ordering, ['setUpClass', 'cleanup_good'])
ordering = []
blowUp = False
runTests(TestableTest)
self.assertEqual(ordering,
['setUpClass', 'test', 'tearDownClass', 'cleanup_good'])
def test_run_class_cleanUp_without_tearDownClass(self):
ordering = []
blowUp = True
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering)
if blowUp:
raise CustomError()
def testNothing(self):
ordering.append('test')
@classmethod
@property
def tearDownClass(cls):
raise AttributeError
runTests(TestableTest)
self.assertEqual(ordering, ['setUpClass', 'cleanup_good'])
ordering = []
blowUp = False
runTests(TestableTest)
self.assertEqual(ordering,
['setUpClass', 'test', 'cleanup_good'])
def test_debug_executes_classCleanUp(self):
ordering = []
blowUp = False
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering, blowUp=blowUp)
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
suite.debug()
self.assertEqual(ordering,
['setUpClass', 'test', 'tearDownClass', 'cleanup_good'])
ordering = []
blowUp = True
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
with self.assertRaises(CustomError) as cm:
suite.debug()
self.assertEqual(str(cm.exception), 'CleanUpExc')
self.assertEqual(ordering,
['setUpClass', 'test', 'tearDownClass', 'cleanup_exc'])
def test_debug_executes_classCleanUp_when_teardown_exception(self):
ordering = []
blowUp = False
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering, blowUp=blowUp)
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
raise CustomError('TearDownClassExc')
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
with self.assertRaises(CustomError) as cm:
suite.debug()
self.assertEqual(str(cm.exception), 'TearDownClassExc')
self.assertEqual(ordering, ['setUpClass', 'test', 'tearDownClass'])
self.assertTrue(TestableTest._class_cleanups)
TestableTest._class_cleanups.clear()
ordering = []
blowUp = True
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
with self.assertRaises(CustomError) as cm:
suite.debug()
self.assertEqual(str(cm.exception), 'TearDownClassExc')
self.assertEqual(ordering, ['setUpClass', 'test', 'tearDownClass'])
self.assertTrue(TestableTest._class_cleanups)
TestableTest._class_cleanups.clear()
def test_doClassCleanups_with_errors_addClassCleanUp(self):
class TestableTest(unittest.TestCase):
def testNothing(self):
pass
def cleanup1():
raise CustomError('cleanup1')
def cleanup2():
raise CustomError('cleanup2')
TestableTest.addClassCleanup(cleanup1)
TestableTest.addClassCleanup(cleanup2)
TestableTest.doClassCleanups()
self.assertEqual(len(TestableTest.tearDown_exceptions), 2)
e1, e2 = TestableTest.tearDown_exceptions
self.assertIsInstance(e1[1], CustomError)
self.assertEqual(str(e1[1]), 'cleanup2')
self.assertIsInstance(e2[1], CustomError)
self.assertEqual(str(e2[1]), 'cleanup1')
def test_with_errors_addCleanUp(self):
ordering = []
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering)
def setUp(self):
ordering.append('setUp')
self.addCleanup(cleanup, ordering, blowUp=True)
def testNothing(self):
pass
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
f'{CustomErrorRepr}: CleanUpExc')
self.assertEqual(ordering,
['setUpClass', 'setUp', 'cleanup_exc',
'tearDownClass', 'cleanup_good'])
def test_run_with_errors_addClassCleanUp(self):
ordering = []
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering, blowUp=True)
def setUp(self):
ordering.append('setUp')
self.addCleanup(cleanup, ordering)
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
f'{CustomErrorRepr}: CleanUpExc')
self.assertEqual(ordering,
['setUpClass', 'setUp', 'test', 'cleanup_good',
'tearDownClass', 'cleanup_exc'])
def test_with_errors_in_addClassCleanup_and_setUps(self):
ordering = []
class_blow_up = False
method_blow_up = False
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering, blowUp=True)
if class_blow_up:
raise CustomError('ClassExc')
def setUp(self):
ordering.append('setUp')
if method_blow_up:
raise CustomError('MethodExc')
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
f'{CustomErrorRepr}: CleanUpExc')
self.assertEqual(ordering,
['setUpClass', 'setUp', 'test',
'tearDownClass', 'cleanup_exc'])
ordering = []
class_blow_up = True
method_blow_up = False
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
f'{CustomErrorRepr}: ClassExc')
self.assertEqual(result.errors[1][1].splitlines()[-1],
f'{CustomErrorRepr}: CleanUpExc')
self.assertEqual(ordering,
['setUpClass', 'cleanup_exc'])
ordering = []
class_blow_up = False
method_blow_up = True
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
f'{CustomErrorRepr}: MethodExc')
self.assertEqual(result.errors[1][1].splitlines()[-1],
f'{CustomErrorRepr}: CleanUpExc')
self.assertEqual(ordering,
['setUpClass', 'setUp', 'tearDownClass',
'cleanup_exc'])
def test_with_errors_in_tearDownClass(self):
ordering = []
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering)
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
raise CustomError('TearDownExc')
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
f'{CustomErrorRepr}: TearDownExc')
self.assertEqual(ordering,
['setUpClass', 'test', 'tearDownClass', 'cleanup_good'])
def test_enterClassContext(self):
class TestableTest(unittest.TestCase):
def testNothing(self):
pass
cleanups = []
TestableTest.addClassCleanup(cleanups.append, 'cleanup1')
cm = TestCM(cleanups, 42)
self.assertEqual(TestableTest.enterClassContext(cm), 42)
TestableTest.addClassCleanup(cleanups.append, 'cleanup2')
TestableTest.doClassCleanups()
self.assertEqual(cleanups, ['enter', 'cleanup2', 'exit', 'cleanup1'])
def test_enterClassContext_arg_errors(self):
class TestableTest(unittest.TestCase):
def testNothing(self):
pass
with self.assertRaisesRegex(TypeError, 'the context manager'):
TestableTest.enterClassContext(LacksEnterAndExit())
with self.assertRaisesRegex(TypeError, 'the context manager'):
TestableTest.enterClassContext(LacksEnter())
with self.assertRaisesRegex(TypeError, 'the context manager'):
TestableTest.enterClassContext(LacksExit())
self.assertEqual(TestableTest._class_cleanups, [])
def test_run_nested_test(self):
ordering = []
class InnerTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('inner setup')
cls.addClassCleanup(ordering.append, 'inner cleanup')
def test(self):
ordering.append('inner test')
class OuterTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('outer setup')
cls.addClassCleanup(ordering.append, 'outer cleanup')
def test(self):
ordering.append('start outer test')
runTests(InnerTest)
ordering.append('end outer test')
runTests(OuterTest)
self.assertEqual(ordering, [
'outer setup', 'start outer test',
'inner setup', 'inner test', 'inner cleanup',
'end outer test', 'outer cleanup'])
def test_run_empty_suite_error_message(self):
class EmptyTest(unittest.TestCase):
pass
suite = unittest.defaultTestLoader.loadTestsFromTestCase(EmptyTest)
runner = getRunner()
runner.run(suite)
self.assertIn("\nNO TESTS RAN\n", runner.stream.getvalue())
class TestModuleCleanUp(unittest.TestCase):
def test_add_and_do_ModuleCleanup(self):
module_cleanups = []
def module_cleanup1(*args, **kwargs):
module_cleanups.append((3, args, kwargs))
def module_cleanup2(*args, **kwargs):
module_cleanups.append((4, args, kwargs))
class Module(object):
unittest.addModuleCleanup(module_cleanup1, 1, 2, 3,
four='hello', five='goodbye')
unittest.addModuleCleanup(module_cleanup2)
self.assertEqual(unittest.case._module_cleanups,
[(module_cleanup1, (1, 2, 3),
dict(four='hello', five='goodbye')),
(module_cleanup2, (), {})])
unittest.case.doModuleCleanups()
self.assertEqual(module_cleanups, [(4, (), {}), (3, (1, 2, 3),
dict(four='hello', five='goodbye'))])
self.assertEqual(unittest.case._module_cleanups, [])
def test_doModuleCleanup_with_errors_in_addModuleCleanup(self):
module_cleanups = []
def module_cleanup_good(*args, **kwargs):
module_cleanups.append((3, args, kwargs))
def module_cleanup_bad(*args, **kwargs):
raise CustomError('CleanUpExc')
class Module(object):
unittest.addModuleCleanup(module_cleanup_good, 1, 2, 3,
four='hello', five='goodbye')
unittest.addModuleCleanup(module_cleanup_bad)
self.assertEqual(unittest.case._module_cleanups,
[(module_cleanup_good, (1, 2, 3),
dict(four='hello', five='goodbye')),
(module_cleanup_bad, (), {})])
with self.assertRaises(CustomError) as e:
unittest.case.doModuleCleanups()
self.assertEqual(str(e.exception), 'CleanUpExc')
self.assertEqual(unittest.case._module_cleanups, [])
def test_addModuleCleanup_arg_errors(self):
cleanups = []
def cleanup(*args, **kwargs):
cleanups.append((args, kwargs))
class Module(object):
unittest.addModuleCleanup(cleanup, 1, 2, function='hello')
with self.assertRaises(TypeError):
unittest.addModuleCleanup(function=cleanup, arg='hello')
with self.assertRaises(TypeError):
unittest.addModuleCleanup()
unittest.case.doModuleCleanups()
self.assertEqual(cleanups,
[((1, 2), {'function': 'hello'})])
def test_run_module_cleanUp(self):
blowUp = True
ordering = []
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering)
if blowUp:
raise CustomError('setUpModule Exc')
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
TestableTest.__module__ = 'Module'
sys.modules['Module'] = Module
result = runTests(TestableTest)
self.assertEqual(ordering, ['setUpModule', 'cleanup_good'])
self.assertEqual(result.errors[0][1].splitlines()[-1],
f'{CustomErrorRepr}: setUpModule Exc')
ordering = []
blowUp = False
runTests(TestableTest)
self.assertEqual(ordering,
['setUpModule', 'setUpClass', 'test', 'tearDownClass',
'tearDownModule', 'cleanup_good'])
self.assertEqual(unittest.case._module_cleanups, [])
def test_run_multiple_module_cleanUp(self):
blowUp = True
blowUp2 = False
ordering = []
class Module1(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering)
if blowUp:
raise CustomError()
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
class Module2(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule2')
unittest.addModuleCleanup(cleanup, ordering)
if blowUp2:
raise CustomError()
@staticmethod
def tearDownModule():
ordering.append('tearDownModule2')
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
class TestableTest2(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass2')
def testNothing(self):
ordering.append('test2')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass2')
TestableTest.__module__ = 'Module1'
sys.modules['Module1'] = Module1
TestableTest2.__module__ = 'Module2'
sys.modules['Module2'] = Module2
runTests(TestableTest, TestableTest2)
self.assertEqual(ordering, ['setUpModule', 'cleanup_good',
'setUpModule2', 'setUpClass2', 'test2',
'tearDownClass2', 'tearDownModule2',
'cleanup_good'])
ordering = []
blowUp = False
blowUp2 = True
runTests(TestableTest, TestableTest2)
self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test',
'tearDownClass', 'tearDownModule',
'cleanup_good', 'setUpModule2',
'cleanup_good'])
ordering = []
blowUp = False
blowUp2 = False
runTests(TestableTest, TestableTest2)
self.assertEqual(ordering,
['setUpModule', 'setUpClass', 'test', 'tearDownClass',
'tearDownModule', 'cleanup_good', 'setUpModule2',
'setUpClass2', 'test2', 'tearDownClass2',
'tearDownModule2', 'cleanup_good'])
self.assertEqual(unittest.case._module_cleanups, [])
def test_run_module_cleanUp_without_teardown(self):
ordering = []
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering)
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
TestableTest.__module__ = 'Module'
sys.modules['Module'] = Module
runTests(TestableTest)
self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test',
'tearDownClass', 'cleanup_good'])
self.assertEqual(unittest.case._module_cleanups, [])
def test_run_module_cleanUp_when_teardown_exception(self):
ordering = []
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering)
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
raise CustomError('CleanUpExc')
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
TestableTest.__module__ = 'Module'
sys.modules['Module'] = Module
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
f'{CustomErrorRepr}: CleanUpExc')
self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test',
'tearDownClass', 'tearDownModule',
'cleanup_good'])
self.assertEqual(unittest.case._module_cleanups, [])
def test_debug_module_executes_cleanUp(self):
ordering = []
blowUp = False
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering, blowUp=blowUp)
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
TestableTest.__module__ = 'Module'
sys.modules['Module'] = Module
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
suite.debug()
self.assertEqual(ordering,
['setUpModule', 'setUpClass', 'test', 'tearDownClass',
'tearDownModule', 'cleanup_good'])
self.assertEqual(unittest.case._module_cleanups, [])
ordering = []
blowUp = True
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
with self.assertRaises(CustomError) as cm:
suite.debug()
self.assertEqual(str(cm.exception), 'CleanUpExc')
self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test',
'tearDownClass', 'tearDownModule', 'cleanup_exc'])
self.assertEqual(unittest.case._module_cleanups, [])
def test_debug_module_cleanUp_when_teardown_exception(self):
ordering = []
blowUp = False
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering, blowUp=blowUp)
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
raise CustomError('TearDownModuleExc')
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
TestableTest.__module__ = 'Module'
sys.modules['Module'] = Module
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
with self.assertRaises(CustomError) as cm:
suite.debug()
self.assertEqual(str(cm.exception), 'TearDownModuleExc')
self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test',
'tearDownClass', 'tearDownModule'])
self.assertTrue(unittest.case._module_cleanups)
unittest.case._module_cleanups.clear()
ordering = []
blowUp = True
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
with self.assertRaises(CustomError) as cm:
suite.debug()
self.assertEqual(str(cm.exception), 'TearDownModuleExc')
self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test',
'tearDownClass', 'tearDownModule'])
self.assertTrue(unittest.case._module_cleanups)
unittest.case._module_cleanups.clear()
def test_addClassCleanup_arg_errors(self):
cleanups = []
def cleanup(*args, **kwargs):
cleanups.append((args, kwargs))
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.addClassCleanup(cleanup, 1, 2, function=3, cls=4)
with self.assertRaises(TypeError):
cls.addClassCleanup(function=cleanup, arg='hello')
def testNothing(self):
pass
with self.assertRaises(TypeError):
TestableTest.addClassCleanup()
with self.assertRaises(TypeError):
unittest.TestCase.addCleanup(cls=TestableTest(), function=cleanup)
runTests(TestableTest)
self.assertEqual(cleanups,
[((1, 2), {'function': 3, 'cls': 4})])
def test_addCleanup_arg_errors(self):
cleanups = []
def cleanup(*args, **kwargs):
cleanups.append((args, kwargs))
class TestableTest(unittest.TestCase):
def setUp(self2):
self2.addCleanup(cleanup, 1, 2, function=3, self=4)
with self.assertRaises(TypeError):
self2.addCleanup(function=cleanup, arg='hello')
def testNothing(self):
pass
with self.assertRaises(TypeError):
TestableTest().addCleanup()
with self.assertRaises(TypeError):
unittest.TestCase.addCleanup(self=TestableTest(), function=cleanup)
runTests(TestableTest)
self.assertEqual(cleanups,
[((1, 2), {'function': 3, 'self': 4})])
def test_with_errors_in_addClassCleanup(self):
ordering = []
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering)
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering, blowUp=True)
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
TestableTest.__module__ = 'Module'
sys.modules['Module'] = Module
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
f'{CustomErrorRepr}: CleanUpExc')
self.assertEqual(ordering,
['setUpModule', 'setUpClass', 'test', 'tearDownClass',
'cleanup_exc', 'tearDownModule', 'cleanup_good'])
def test_with_errors_in_addCleanup(self):
ordering = []
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering)
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
class TestableTest(unittest.TestCase):
def setUp(self):
ordering.append('setUp')
self.addCleanup(cleanup, ordering, blowUp=True)
def testNothing(self):
ordering.append('test')
def tearDown(self):
ordering.append('tearDown')
TestableTest.__module__ = 'Module'
sys.modules['Module'] = Module
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
f'{CustomErrorRepr}: CleanUpExc')
self.assertEqual(ordering,
['setUpModule', 'setUp', 'test', 'tearDown',
'cleanup_exc', 'tearDownModule', 'cleanup_good'])
def test_with_errors_in_addModuleCleanup_and_setUps(self):
ordering = []
module_blow_up = False
class_blow_up = False
method_blow_up = False
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering, blowUp=True)
if module_blow_up:
raise CustomError('ModuleExc')
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
if class_blow_up:
raise CustomError('ClassExc')
def setUp(self):
ordering.append('setUp')
if method_blow_up:
raise CustomError('MethodExc')
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
TestableTest.__module__ = 'Module'
sys.modules['Module'] = Module
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
f'{CustomErrorRepr}: CleanUpExc')
self.assertEqual(ordering,
['setUpModule', 'setUpClass', 'setUp', 'test',
'tearDownClass', 'tearDownModule',
'cleanup_exc'])
ordering = []
module_blow_up = True
class_blow_up = False
method_blow_up = False
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
f'{CustomErrorRepr}: ModuleExc')
self.assertEqual(result.errors[1][1].splitlines()[-1],
f'{CustomErrorRepr}: CleanUpExc')
self.assertEqual(ordering, ['setUpModule', 'cleanup_exc'])
ordering = []
module_blow_up = False
class_blow_up = True
method_blow_up = False
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
f'{CustomErrorRepr}: ClassExc')
self.assertEqual(result.errors[1][1].splitlines()[-1],
f'{CustomErrorRepr}: CleanUpExc')
self.assertEqual(ordering, ['setUpModule', 'setUpClass',
'tearDownModule', 'cleanup_exc'])
ordering = []
module_blow_up = False
class_blow_up = False
method_blow_up = True
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
f'{CustomErrorRepr}: MethodExc')
self.assertEqual(result.errors[1][1].splitlines()[-1],
f'{CustomErrorRepr}: CleanUpExc')
self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'setUp',
'tearDownClass', 'tearDownModule',
'cleanup_exc'])
def test_module_cleanUp_with_multiple_classes(self):
ordering =[]
def cleanup1():
ordering.append('cleanup1')
def cleanup2():
ordering.append('cleanup2')
def cleanup3():
ordering.append('cleanup3')
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup1)
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
class TestableTest(unittest.TestCase):
def setUp(self):
ordering.append('setUp')
self.addCleanup(cleanup2)
def testNothing(self):
ordering.append('test')
def tearDown(self):
ordering.append('tearDown')
class OtherTestableTest(unittest.TestCase):
def setUp(self):
ordering.append('setUp2')
self.addCleanup(cleanup3)
def testNothing(self):
ordering.append('test2')
def tearDown(self):
ordering.append('tearDown2')
TestableTest.__module__ = 'Module'
OtherTestableTest.__module__ = 'Module'
sys.modules['Module'] = Module
runTests(TestableTest, OtherTestableTest)
self.assertEqual(ordering,
['setUpModule', 'setUp', 'test', 'tearDown',
'cleanup2', 'setUp2', 'test2', 'tearDown2',
'cleanup3', 'tearDownModule', 'cleanup1'])
def test_enterModuleContext(self):
cleanups = []
unittest.addModuleCleanup(cleanups.append, 'cleanup1')
cm = TestCM(cleanups, 42)
self.assertEqual(unittest.enterModuleContext(cm), 42)
unittest.addModuleCleanup(cleanups.append, 'cleanup2')
unittest.case.doModuleCleanups()
self.assertEqual(cleanups, ['enter', 'cleanup2', 'exit', 'cleanup1'])
def test_enterModuleContext_arg_errors(self):
class TestableTest(unittest.TestCase):
def testNothing(self):
pass
with self.assertRaisesRegex(TypeError, 'the context manager'):
unittest.enterModuleContext(LacksEnterAndExit())
with self.assertRaisesRegex(TypeError, 'the context manager'):
unittest.enterModuleContext(LacksEnter())
with self.assertRaisesRegex(TypeError, 'the context manager'):
unittest.enterModuleContext(LacksExit())
self.assertEqual(unittest.case._module_cleanups, [])
class Test_TextTestRunner(unittest.TestCase):
"""Tests for TextTestRunner."""
def setUp(self):
# clean the environment from pre-existing PYTHONWARNINGS to make
# test_warnings results consistent
self.pythonwarnings = os.environ.get('PYTHONWARNINGS')
if self.pythonwarnings:
del os.environ['PYTHONWARNINGS']
def tearDown(self):
# bring back pre-existing PYTHONWARNINGS if present
if self.pythonwarnings:
os.environ['PYTHONWARNINGS'] = self.pythonwarnings
def test_init(self):
runner = unittest.TextTestRunner()
self.assertFalse(runner.failfast)
self.assertFalse(runner.buffer)
self.assertEqual(runner.verbosity, 1)
self.assertEqual(runner.warnings, None)
self.assertTrue(runner.descriptions)
self.assertEqual(runner.resultclass, unittest.TextTestResult)
self.assertFalse(runner.tb_locals)
self.assertIsNone(runner.durations)
def test_multiple_inheritance(self):
class AResult(unittest.TestResult):
def __init__(self, stream, descriptions, verbosity):
super(AResult, self).__init__(stream, descriptions, verbosity)
class ATextResult(unittest.TextTestResult, AResult):
pass
# This used to raise an exception due to TextTestResult not passing
# on arguments in its __init__ super call
ATextResult(None, None, 1)
def testBufferAndFailfast(self):
class Test(unittest.TestCase):
def testFoo(self):
pass
result = unittest.TestResult()
runner = unittest.TextTestRunner(stream=io.StringIO(), failfast=True,
buffer=True)
# Use our result object
runner._makeResult = lambda: result
runner.run(Test('testFoo'))
self.assertTrue(result.failfast)
self.assertTrue(result.buffer)
def test_locals(self):
runner = unittest.TextTestRunner(stream=io.StringIO(), tb_locals=True)
result = runner.run(unittest.TestSuite())
self.assertEqual(True, result.tb_locals)
def testRunnerRegistersResult(self):
class Test(unittest.TestCase):
def testFoo(self):
pass
originalRegisterResult = unittest.runner.registerResult
def cleanup():
unittest.runner.registerResult = originalRegisterResult
self.addCleanup(cleanup)
result = unittest.TestResult()
runner = unittest.TextTestRunner(stream=io.StringIO())
# Use our result object
runner._makeResult = lambda: result
self.wasRegistered = 0
def fakeRegisterResult(thisResult):
self.wasRegistered += 1
self.assertEqual(thisResult, result)
unittest.runner.registerResult = fakeRegisterResult
runner.run(unittest.TestSuite())
self.assertEqual(self.wasRegistered, 1)
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)
def test_pickle_unpickle(self):
# Issue #7197: a TextTestRunner should be (un)pickleable. This is
# required by test_multiprocessing under Windows (in verbose mode).
stream = io.StringIO("foo")
runner = unittest.TextTestRunner(stream)
for protocol in range(2, pickle.HIGHEST_PROTOCOL + 1):
s = pickle.dumps(runner, protocol)
obj = pickle.loads(s)
# StringIO objects never compare equal, a cheap test instead.
self.assertEqual(obj.stream.getvalue(), stream.getvalue())
def test_resultclass(self):
def MockResultClass(*args):
return args
STREAM = object()
DESCRIPTIONS = object()
VERBOSITY = object()
runner = unittest.TextTestRunner(STREAM, DESCRIPTIONS, VERBOSITY,
resultclass=MockResultClass)
self.assertEqual(runner.resultclass, MockResultClass)
expectedresult = (runner.stream, DESCRIPTIONS, VERBOSITY)
self.assertEqual(runner._makeResult(), expectedresult)
@support.requires_subprocess()
def test_warnings(self):
"""
Check that warnings argument of TextTestRunner correctly affects the
behavior of the warnings.
"""
# see #10535 and the _test_warnings file for more information
def get_parse_out_err(p):
return [b.splitlines() for b in p.communicate()]
opts = dict(stdout=subprocess.PIPE, stderr=subprocess.PIPE,
cwd=os.path.dirname(__file__))
# no args -> all the warnings are printed, unittest warnings only once
p = subprocess.Popen([sys.executable, '-E', '_test_warnings.py'], **opts)
with p:
out, err = get_parse_out_err(p)
self.assertIn(b'OK', err)
# check that the total number of warnings in the output is correct
self.assertEqual(len(out), 10)
# check that the numbers of the different kind of warnings is correct
for msg in [b'dw', b'iw', b'uw']:
self.assertEqual(out.count(msg), 3)
for msg in [b'rw']:
self.assertEqual(out.count(msg), 1)
args_list = (
# passing 'ignore' as warnings arg -> no warnings
[sys.executable, '_test_warnings.py', 'ignore'],
# -W doesn't affect the result if the arg is passed
[sys.executable, '-Wa', '_test_warnings.py', 'ignore'],
# -W affects the result if the arg is not passed
[sys.executable, '-Wi', '_test_warnings.py']
)
# in all these cases no warnings are printed
for args in args_list:
p = subprocess.Popen(args, **opts)
with p:
out, err = get_parse_out_err(p)
self.assertIn(b'OK', err)
self.assertEqual(len(out), 0)
# passing 'always' as warnings arg -> all the warnings printed,
# unittest warnings only once
p = subprocess.Popen([sys.executable, '_test_warnings.py', 'always'],
**opts)
with p:
out, err = get_parse_out_err(p)
self.assertIn(b'OK', err)
self.assertEqual(len(out), 12)
for msg in [b'dw', b'iw', b'uw', b'rw']:
self.assertEqual(out.count(msg), 3)
def testStdErrLookedUpAtInstantiationTime(self):
# see issue 10786
old_stderr = sys.stderr
f = io.StringIO()
sys.stderr = f
try:
runner = unittest.TextTestRunner()
self.assertTrue(runner.stream.stream is f)
finally:
sys.stderr = old_stderr
def testSpecifiedStreamUsed(self):
# see issue 10786
f = io.StringIO()
runner = unittest.TextTestRunner(f)
self.assertTrue(runner.stream.stream is f)
def test_durations(self):
def run(test, *, expect_durations=True):
stream = BufferedWriter()
runner = unittest.TextTestRunner(stream=stream, durations=5, verbosity=2)
result = runner.run(test)
self.assertEqual(result.durations, 5)
stream.flush()
text = stream.getvalue()
regex = r"\n\d+.\d\d\ds"
if expect_durations:
self.assertEqual(len(result.collectedDurations), 1)
self.assertIn('Slowest test durations', text)
self.assertRegex(text, regex)
else:
self.assertEqual(len(result.collectedDurations), 0)
self.assertNotIn('Slowest test durations', text)
self.assertNotRegex(text, regex)
# success
class Foo(unittest.TestCase):
def test_1(self):
pass
run(Foo('test_1'), expect_durations=True)
# failure
class Foo(unittest.TestCase):
def test_1(self):
self.assertEqual(0, 1)
run(Foo('test_1'), expect_durations=True)
# error
class Foo(unittest.TestCase):
def test_1(self):
1 / 0
run(Foo('test_1'), expect_durations=True)
# error in setUp and tearDown
class Foo(unittest.TestCase):
def setUp(self):
1 / 0
tearDown = setUp
def test_1(self):
pass
run(Foo('test_1'), expect_durations=True)
# skip (expect no durations)
class Foo(unittest.TestCase):
@unittest.skip("reason")
def test_1(self):
pass
run(Foo('test_1'), expect_durations=False)
if __name__ == "__main__":
unittest.main()