aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/unittest/test
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/unittest/test')
-rw-r--r--Lib/unittest/test/__main__.py18
-rw-r--r--Lib/unittest/test/support.py38
-rw-r--r--Lib/unittest/test/test_assertions.py4
-rw-r--r--Lib/unittest/test/test_break.py4
-rw-r--r--Lib/unittest/test/test_case.py255
-rw-r--r--Lib/unittest/test/test_discovery.py169
-rw-r--r--Lib/unittest/test/test_functiontestcase.py4
-rw-r--r--Lib/unittest/test/test_loader.py4
-rw-r--r--Lib/unittest/test/test_program.py72
-rw-r--r--Lib/unittest/test/test_result.py92
-rw-r--r--Lib/unittest/test/test_runner.py16
-rw-r--r--Lib/unittest/test/test_setups.py7
-rw-r--r--Lib/unittest/test/test_skipping.py80
-rw-r--r--Lib/unittest/test/test_suite.py81
-rw-r--r--Lib/unittest/test/testmock/__main__.py18
-rw-r--r--Lib/unittest/test/testmock/testcallable.py4
-rw-r--r--Lib/unittest/test/testmock/testhelpers.py59
-rw-r--r--Lib/unittest/test/testmock/testmock.py143
-rw-r--r--Lib/unittest/test/testmock/testpatch.py1
-rw-r--r--Lib/unittest/test/testmock/testwith.py83
20 files changed, 1045 insertions, 107 deletions
diff --git a/Lib/unittest/test/__main__.py b/Lib/unittest/test/__main__.py
new file mode 100644
index 00000000000..44d0591e847
--- /dev/null
+++ b/Lib/unittest/test/__main__.py
@@ -0,0 +1,18 @@
+import os
+import unittest
+
+
+def load_tests(loader, standard_tests, pattern):
+ # top level directory cached on loader instance
+ this_dir = os.path.dirname(__file__)
+ pattern = pattern or "test_*.py"
+ # We are inside unittest.test, so the top-level is two notches up
+ top_level_dir = os.path.dirname(os.path.dirname(this_dir))
+ package_tests = loader.discover(start_dir=this_dir, pattern=pattern,
+ top_level_dir=top_level_dir)
+ standard_tests.addTests(package_tests)
+ return standard_tests
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Lib/unittest/test/support.py b/Lib/unittest/test/support.py
index dbe4ddcd0e7..02e8f3a00b6 100644
--- a/Lib/unittest/test/support.py
+++ b/Lib/unittest/test/support.py
@@ -41,7 +41,7 @@ class TestHashing(object):
self.fail("Problem hashing %s and %s: %s" % (obj_1, obj_2, e))
-class LoggingResult(unittest.TestResult):
+class _BaseLoggingResult(unittest.TestResult):
def __init__(self, log):
self._events = log
super().__init__()
@@ -52,7 +52,7 @@ class LoggingResult(unittest.TestResult):
def startTestRun(self):
self._events.append('startTestRun')
- super(LoggingResult, self).startTestRun()
+ super().startTestRun()
def stopTest(self, test):
self._events.append('stopTest')
@@ -60,7 +60,7 @@ class LoggingResult(unittest.TestResult):
def stopTestRun(self):
self._events.append('stopTestRun')
- super(LoggingResult, self).stopTestRun()
+ super().stopTestRun()
def addFailure(self, *args):
self._events.append('addFailure')
@@ -68,7 +68,7 @@ class LoggingResult(unittest.TestResult):
def addSuccess(self, *args):
self._events.append('addSuccess')
- super(LoggingResult, self).addSuccess(*args)
+ super().addSuccess(*args)
def addError(self, *args):
self._events.append('addError')
@@ -76,15 +76,39 @@ class LoggingResult(unittest.TestResult):
def addSkip(self, *args):
self._events.append('addSkip')
- super(LoggingResult, self).addSkip(*args)
+ super().addSkip(*args)
def addExpectedFailure(self, *args):
self._events.append('addExpectedFailure')
- super(LoggingResult, self).addExpectedFailure(*args)
+ super().addExpectedFailure(*args)
def addUnexpectedSuccess(self, *args):
self._events.append('addUnexpectedSuccess')
- super(LoggingResult, self).addUnexpectedSuccess(*args)
+ super().addUnexpectedSuccess(*args)
+
+
+class LegacyLoggingResult(_BaseLoggingResult):
+ """
+ A legacy TestResult implementation, without an addSubTest method,
+ which records its method calls.
+ """
+
+ @property
+ def addSubTest(self):
+ raise AttributeError
+
+
+class LoggingResult(_BaseLoggingResult):
+ """
+ A TestResult implementation which records its method calls.
+ """
+
+ def addSubTest(self, test, subtest, err):
+ if err is None:
+ self._events.append('addSubTestSuccess')
+ else:
+ self._events.append('addSubTestFailure')
+ super().addSubTest(test, subtest, err)
class ResultWithNoStartTestRunStopTestRun(object):
diff --git a/Lib/unittest/test/test_assertions.py b/Lib/unittest/test/test_assertions.py
index 7931cadaf3d..af08d5ad65a 100644
--- a/Lib/unittest/test/test_assertions.py
+++ b/Lib/unittest/test/test_assertions.py
@@ -361,3 +361,7 @@ class TestLongMessage(unittest.TestCase):
['^"regex" does not match "foo"$', '^oops$',
'^"regex" does not match "foo"$',
'^"regex" does not match "foo" : oops$'])
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/unittest/test/test_break.py b/Lib/unittest/test/test_break.py
index 75532f42d3d..0bf1a229b84 100644
--- a/Lib/unittest/test/test_break.py
+++ b/Lib/unittest/test/test_break.py
@@ -282,3 +282,7 @@ class TestBreakSignalIgnored(TestBreak):
"if threads have been used")
class TestBreakSignalDefault(TestBreak):
int_handler = signal.SIG_DFL
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/unittest/test/test_case.py b/Lib/unittest/test/test_case.py
index b8cb0c76b7c..363390af09c 100644
--- a/Lib/unittest/test/test_case.py
+++ b/Lib/unittest/test/test_case.py
@@ -1,8 +1,10 @@
+import contextlib
import difflib
import pprint
import pickle
import re
import sys
+import logging
import warnings
import weakref
import inspect
@@ -13,9 +15,15 @@ from test import support
import unittest
from .support import (
- TestEquality, TestHashing, LoggingResult,
+ TestEquality, TestHashing, LoggingResult, LegacyLoggingResult,
ResultWithNoStartTestRunStopTestRun
)
+from test.support import captured_stderr
+
+
+log_foo = logging.getLogger('foo')
+log_foobar = logging.getLogger('foo.bar')
+log_quux = logging.getLogger('quux')
class Test(object):
@@ -297,6 +305,98 @@ class Test_TestCase(unittest.TestCase, TestEquality, TestHashing):
Foo('test').run()
+ def _check_call_order__subtests(self, result, events, expected_events):
+ class Foo(Test.LoggingTestCase):
+ def test(self):
+ super(Foo, self).test()
+ for i in [1, 2, 3]:
+ with self.subTest(i=i):
+ if i == 1:
+ self.fail('failure')
+ for j in [2, 3]:
+ with self.subTest(j=j):
+ if i * j == 6:
+ raise RuntimeError('raised by Foo.test')
+ 1 / 0
+
+ # Order is the following:
+ # i=1 => subtest failure
+ # i=2, j=2 => subtest success
+ # i=2, j=3 => subtest error
+ # i=3, j=2 => subtest error
+ # i=3, j=3 => subtest success
+ # toplevel => error
+ Foo(events).run(result)
+ self.assertEqual(events, expected_events)
+
+ def test_run_call_order__subtests(self):
+ events = []
+ result = LoggingResult(events)
+ expected = ['startTest', 'setUp', 'test', 'tearDown',
+ 'addSubTestFailure', 'addSubTestSuccess',
+ 'addSubTestFailure', 'addSubTestFailure',
+ 'addSubTestSuccess', 'addError', 'stopTest']
+ self._check_call_order__subtests(result, events, expected)
+
+ def test_run_call_order__subtests_legacy(self):
+ # With a legacy result object (without a addSubTest method),
+ # text execution stops after the first subtest failure.
+ events = []
+ result = LegacyLoggingResult(events)
+ expected = ['startTest', 'setUp', 'test', 'tearDown',
+ 'addFailure', 'stopTest']
+ self._check_call_order__subtests(result, events, expected)
+
+ def _check_call_order__subtests_success(self, result, events, expected_events):
+ class Foo(Test.LoggingTestCase):
+ def test(self):
+ super(Foo, self).test()
+ for i in [1, 2]:
+ with self.subTest(i=i):
+ for j in [2, 3]:
+ with self.subTest(j=j):
+ pass
+
+ Foo(events).run(result)
+ self.assertEqual(events, expected_events)
+
+ def test_run_call_order__subtests_success(self):
+ events = []
+ result = LoggingResult(events)
+ # The 6 subtest successes are individually recorded, in addition
+ # to the whole test success.
+ expected = (['startTest', 'setUp', 'test', 'tearDown']
+ + 6 * ['addSubTestSuccess']
+ + ['addSuccess', 'stopTest'])
+ self._check_call_order__subtests_success(result, events, expected)
+
+ def test_run_call_order__subtests_success_legacy(self):
+ # With a legacy result, only the whole test success is recorded.
+ events = []
+ result = LegacyLoggingResult(events)
+ expected = ['startTest', 'setUp', 'test', 'tearDown',
+ 'addSuccess', 'stopTest']
+ self._check_call_order__subtests_success(result, events, expected)
+
+ def test_run_call_order__subtests_failfast(self):
+ events = []
+ result = LoggingResult(events)
+ result.failfast = True
+
+ class Foo(Test.LoggingTestCase):
+ def test(self):
+ super(Foo, self).test()
+ with self.subTest(i=1):
+ self.fail('failure')
+ with self.subTest(i=2):
+ self.fail('failure')
+ self.fail('failure')
+
+ expected = ['startTest', 'setUp', 'test', 'tearDown',
+ 'addSubTestFailure', 'stopTest']
+ Foo(events).run(result)
+ self.assertEqual(events, expected)
+
# "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
@@ -729,18 +829,18 @@ class Test_TestCase(unittest.TestCase, TestEquality, TestHashing):
# set a lower threshold value and add a cleanup to restore it
old_threshold = self._diffThreshold
- self._diffThreshold = 2**8
+ self._diffThreshold = 2**5
self.addCleanup(lambda: setattr(self, '_diffThreshold', old_threshold))
# under the threshold: diff marker (^) in error message
- s = 'x' * (2**7)
+ s = 'x' * (2**4)
with self.assertRaises(self.failureException) as cm:
self.assertEqual(s + 'a', s + 'b')
self.assertIn('^', str(cm.exception))
self.assertEqual(s + 'a', s + 'a')
# over the threshold: diff not used and marker (^) not in error message
- s = 'x' * (2**9)
+ s = 'x' * (2**6)
# if the path that uses difflib is taken, _truncateMessage will be
# called -- replace it with explodingTruncation to verify that this
# doesn't happen
@@ -757,6 +857,35 @@ class Test_TestCase(unittest.TestCase, TestEquality, TestHashing):
self.assertEqual(str(cm.exception), '%r != %r' % (s1, s2))
self.assertEqual(s + 'a', s + 'a')
+ def testAssertEqual_shorten(self):
+ # set a lower threshold value and add a cleanup to restore it
+ old_threshold = self._diffThreshold
+ self._diffThreshold = 0
+ self.addCleanup(lambda: setattr(self, '_diffThreshold', old_threshold))
+
+ s = 'x' * 100
+ s1, s2 = s + 'a', s + 'b'
+ with self.assertRaises(self.failureException) as cm:
+ self.assertEqual(s1, s2)
+ c = 'xxxx[35 chars]' + 'x' * 61
+ self.assertEqual(str(cm.exception), "'%sa' != '%sb'" % (c, c))
+ self.assertEqual(s + 'a', s + 'a')
+
+ p = 'y' * 50
+ s1, s2 = s + 'a' + p, s + 'b' + p
+ with self.assertRaises(self.failureException) as cm:
+ self.assertEqual(s1, s2)
+ c = 'xxxx[85 chars]xxxxxxxxxxx'
+ self.assertEqual(str(cm.exception), "'%sa%s' != '%sb%s'" % (c, p, c, p))
+
+ p = 'y' * 100
+ s1, s2 = s + 'a' + p, s + 'b' + p
+ with self.assertRaises(self.failureException) as cm:
+ self.assertEqual(s1, s2)
+ c = 'xxxx[91 chars]xxxxx'
+ d = 'y' * 40 + '[56 chars]yyyy'
+ self.assertEqual(str(cm.exception), "'%sa%s' != '%sb%s'" % (c, d, c, d))
+
def testAssertCountEqual(self):
a = object()
self.assertCountEqual([1, 2, 3], [3, 2, 1])
@@ -1159,6 +1288,94 @@ test case
with self.assertWarnsRegex(RuntimeWarning, "o+"):
_runtime_warn("barz")
+ @contextlib.contextmanager
+ def assertNoStderr(self):
+ with captured_stderr() as buf:
+ yield
+ self.assertEqual(buf.getvalue(), "")
+
+ def assertLogRecords(self, records, matches):
+ self.assertEqual(len(records), len(matches))
+ for rec, match in zip(records, matches):
+ self.assertIsInstance(rec, logging.LogRecord)
+ for k, v in match.items():
+ self.assertEqual(getattr(rec, k), v)
+
+ def testAssertLogsDefaults(self):
+ # defaults: root logger, level INFO
+ with self.assertNoStderr():
+ with self.assertLogs() as cm:
+ log_foo.info("1")
+ log_foobar.debug("2")
+ self.assertEqual(cm.output, ["INFO:foo:1"])
+ self.assertLogRecords(cm.records, [{'name': 'foo'}])
+
+ def testAssertLogsTwoMatchingMessages(self):
+ # Same, but with two matching log messages
+ with self.assertNoStderr():
+ with self.assertLogs() as cm:
+ log_foo.info("1")
+ log_foobar.debug("2")
+ log_quux.warning("3")
+ self.assertEqual(cm.output, ["INFO:foo:1", "WARNING:quux:3"])
+ self.assertLogRecords(cm.records,
+ [{'name': 'foo'}, {'name': 'quux'}])
+
+ def checkAssertLogsPerLevel(self, level):
+ # Check level filtering
+ with self.assertNoStderr():
+ with self.assertLogs(level=level) as cm:
+ log_foo.warning("1")
+ log_foobar.error("2")
+ log_quux.critical("3")
+ self.assertEqual(cm.output, ["ERROR:foo.bar:2", "CRITICAL:quux:3"])
+ self.assertLogRecords(cm.records,
+ [{'name': 'foo.bar'}, {'name': 'quux'}])
+
+ def testAssertLogsPerLevel(self):
+ self.checkAssertLogsPerLevel(logging.ERROR)
+ self.checkAssertLogsPerLevel('ERROR')
+
+ def checkAssertLogsPerLogger(self, logger):
+ # Check per-logger fitering
+ with self.assertNoStderr():
+ with self.assertLogs(level='DEBUG') as outer_cm:
+ with self.assertLogs(logger, level='DEBUG') as cm:
+ log_foo.info("1")
+ log_foobar.debug("2")
+ log_quux.warning("3")
+ self.assertEqual(cm.output, ["INFO:foo:1", "DEBUG:foo.bar:2"])
+ self.assertLogRecords(cm.records,
+ [{'name': 'foo'}, {'name': 'foo.bar'}])
+ # The outer catchall caught the quux log
+ self.assertEqual(outer_cm.output, ["WARNING:quux:3"])
+
+ def testAssertLogsPerLogger(self):
+ self.checkAssertLogsPerLogger(logging.getLogger('foo'))
+ self.checkAssertLogsPerLogger('foo')
+
+ def testAssertLogsFailureNoLogs(self):
+ # Failure due to no logs
+ with self.assertNoStderr():
+ with self.assertRaises(self.failureException):
+ with self.assertLogs():
+ pass
+
+ def testAssertLogsFailureLevelTooHigh(self):
+ # Failure due to level too high
+ with self.assertNoStderr():
+ with self.assertRaises(self.failureException):
+ with self.assertLogs(level='WARNING'):
+ log_foo.info("1")
+
+ def testAssertLogsFailureMismatchingLogger(self):
+ # Failure due to mismatching logger (and the logged message is
+ # passed through)
+ with self.assertLogs('quux', level='ERROR'):
+ with self.assertRaises(self.failureException):
+ with self.assertLogs('foo'):
+ log_quux.error("1")
+
def testDeprecatedMethodNames(self):
"""
Test that the deprecated methods raise a DeprecationWarning. See #9424.
@@ -1313,3 +1530,33 @@ test case
with support.disable_gc():
del case
self.assertFalse(wr())
+
+ def test_no_exception_leak(self):
+ # Issue #19880: TestCase.run() should not keep a reference
+ # to the exception
+ class MyException(Exception):
+ ninstance = 0
+
+ def __init__(self):
+ MyException.ninstance += 1
+ Exception.__init__(self)
+
+ def __del__(self):
+ MyException.ninstance -= 1
+
+ class TestCase(unittest.TestCase):
+ def test1(self):
+ raise MyException()
+
+ @unittest.expectedFailure
+ def test2(self):
+ raise MyException()
+
+ for method_name in ('test1', 'test2'):
+ testcase = TestCase(method_name)
+ testcase.run()
+ self.assertEqual(MyException.ninstance, 0)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/unittest/test/test_discovery.py b/Lib/unittest/test/test_discovery.py
index ccc7db249e4..6b7b1280f8b 100644
--- a/Lib/unittest/test/test_discovery.py
+++ b/Lib/unittest/test/test_discovery.py
@@ -1,12 +1,15 @@
import os
import re
import sys
+import types
+import builtins
+from test import support
import unittest
class TestableTestProgram(unittest.TestProgram):
- module = '__main__'
+ module = None
exit = True
defaultTest = failfast = catchbreak = buffer = None
verbosity = 1
@@ -46,9 +49,9 @@ class TestDiscovery(unittest.TestCase):
def restore_isdir():
os.path.isdir = original_isdir
- path_lists = [['test1.py', 'test2.py', 'not_a_test.py', 'test_dir',
+ path_lists = [['test2.py', 'test1.py', 'not_a_test.py', 'test_dir',
'test.foo', 'test-not-a-module.py', 'another_dir'],
- ['test3.py', 'test4.py', ]]
+ ['test4.py', 'test3.py', ]]
os.listdir = lambda path: path_lists.pop(0)
self.addCleanup(restore_listdir)
@@ -70,6 +73,8 @@ class TestDiscovery(unittest.TestCase):
loader._top_level_dir = top_level
suite = list(loader._find_tests(top_level, 'test*.py'))
+ # The test suites found should be sorted alphabetically for reliable
+ # execution order.
expected = [name + ' module tests' for name in
('test1', 'test2')]
expected.extend([('test_dir.%s' % name) + ' module tests' for name in
@@ -132,6 +137,7 @@ class TestDiscovery(unittest.TestCase):
# and directly from the test_directory2 package
self.assertEqual(suite,
['load_tests', 'test_directory2' + ' module tests'])
+ # The test module paths should be sorted for reliable execution order
self.assertEqual(Module.paths, ['test_directory', 'test_directory2'])
# load_tests should have been called once with loader, tests and pattern
@@ -169,7 +175,7 @@ class TestDiscovery(unittest.TestCase):
self.addCleanup(restore_isdir)
_find_tests_args = []
- def _find_tests(start_dir, pattern):
+ def _find_tests(start_dir, pattern, namespace=None):
_find_tests_args.append((start_dir, pattern))
return ['tests']
loader._find_tests = _find_tests
@@ -184,11 +190,9 @@ class TestDiscovery(unittest.TestCase):
self.assertEqual(_find_tests_args, [(start_dir, 'pattern')])
self.assertIn(top_level_dir, sys.path)
- def test_discover_with_modules_that_fail_to_import(self):
- loader = unittest.TestLoader()
-
+ def setup_import_issue_tests(self, fakefile):
listdir = os.listdir
- os.listdir = lambda _: ['test_this_does_not_exist.py']
+ os.listdir = lambda _: [fakefile]
isfile = os.path.isfile
os.path.isfile = lambda _: True
orig_sys_path = sys.path[:]
@@ -198,6 +202,11 @@ class TestDiscovery(unittest.TestCase):
sys.path[:] = orig_sys_path
self.addCleanup(restore)
+ def test_discover_with_modules_that_fail_to_import(self):
+ loader = unittest.TestLoader()
+
+ self.setup_import_issue_tests('test_this_does_not_exist.py')
+
suite = loader.discover('.')
self.assertIn(os.getcwd(), sys.path)
self.assertEqual(suite.countTestCases(), 1)
@@ -206,62 +215,74 @@ class TestDiscovery(unittest.TestCase):
with self.assertRaises(ImportError):
test.test_this_does_not_exist()
+ def test_discover_with_module_that_raises_SkipTest_on_import(self):
+ loader = unittest.TestLoader()
+
+ def _get_module_from_name(name):
+ raise unittest.SkipTest('skipperoo')
+ loader._get_module_from_name = _get_module_from_name
+
+ self.setup_import_issue_tests('test_skip_dummy.py')
+
+ suite = loader.discover('.')
+ self.assertEqual(suite.countTestCases(), 1)
+
+ result = unittest.TestResult()
+ suite.run(result)
+ self.assertEqual(len(result.skipped), 1)
+
def test_command_line_handling_parseArgs(self):
program = TestableTestProgram()
args = []
- def do_discovery(argv):
- args.extend(argv)
- program._do_discovery = do_discovery
+ program._do_discovery = args.append
program.parseArgs(['something', 'discover'])
- self.assertEqual(args, [])
+ self.assertEqual(args, [[]])
+ args[:] = []
program.parseArgs(['something', 'discover', 'foo', 'bar'])
- self.assertEqual(args, ['foo', 'bar'])
+ self.assertEqual(args, [['foo', 'bar']])
def test_command_line_handling_discover_by_default(self):
program = TestableTestProgram()
- program.module = None
- self.called = False
- def do_discovery(argv):
- self.called = True
- self.assertEqual(argv, [])
- program._do_discovery = do_discovery
+ args = []
+ program._do_discovery = args.append
program.parseArgs(['something'])
- self.assertTrue(self.called)
+ self.assertEqual(args, [[]])
+ self.assertEqual(program.verbosity, 1)
+ self.assertIs(program.buffer, False)
+ self.assertIs(program.catchbreak, False)
+ self.assertIs(program.failfast, False)
def test_command_line_handling_discover_by_default_with_options(self):
program = TestableTestProgram()
- program.module = None
- args = ['something', '-v', '-b', '-v', '-c', '-f']
- self.called = False
- def do_discovery(argv):
- self.called = True
- self.assertEqual(argv, args[1:])
- program._do_discovery = do_discovery
- program.parseArgs(args)
- self.assertTrue(self.called)
+ args = []
+ program._do_discovery = args.append
+ program.parseArgs(['something', '-v', '-b', '-v', '-c', '-f'])
+ self.assertEqual(args, [[]])
+ self.assertEqual(program.verbosity, 2)
+ self.assertIs(program.buffer, True)
+ self.assertIs(program.catchbreak, True)
+ self.assertIs(program.failfast, True)
def test_command_line_handling_do_discovery_too_many_arguments(self):
- class Stop(Exception):
- pass
- def usageExit():
- raise Stop
-
program = TestableTestProgram()
- program.usageExit = usageExit
program.testLoader = None
- with self.assertRaises(Stop):
+ with support.captured_stderr() as stderr, \
+ self.assertRaises(SystemExit) as cm:
# too many args
program._do_discovery(['one', 'two', 'three', 'four'])
+ self.assertEqual(cm.exception.args, (2,))
+ self.assertIn('usage:', stderr.getvalue())
def test_command_line_handling_do_discovery_uses_default_loader(self):
program = object.__new__(unittest.TestProgram)
+ program._initArgParsers()
class Loader(object):
args = []
@@ -417,7 +438,7 @@ class TestDiscovery(unittest.TestCase):
expectedPath = os.path.abspath(os.path.dirname(unittest.test.__file__))
self.wasRun = False
- def _find_tests(start_dir, pattern):
+ def _find_tests(start_dir, pattern, namespace=None):
self.wasRun = True
self.assertEqual(start_dir, expectedPath)
return tests
@@ -427,5 +448,79 @@ class TestDiscovery(unittest.TestCase):
self.assertEqual(suite._tests, tests)
+ def test_discovery_from_dotted_path_builtin_modules(self):
+
+ loader = unittest.TestLoader()
+
+ listdir = os.listdir
+ os.listdir = lambda _: ['test_this_does_not_exist.py']
+ isfile = os.path.isfile
+ isdir = os.path.isdir
+ os.path.isdir = lambda _: False
+ orig_sys_path = sys.path[:]
+ def restore():
+ os.path.isfile = isfile
+ os.path.isdir = isdir
+ os.listdir = listdir
+ sys.path[:] = orig_sys_path
+ self.addCleanup(restore)
+
+ with self.assertRaises(TypeError) as cm:
+ loader.discover('sys')
+ self.assertEqual(str(cm.exception),
+ 'Can not use builtin modules '
+ 'as dotted module names')
+
+ def test_discovery_from_dotted_namespace_packages(self):
+ loader = unittest.TestLoader()
+
+ orig_import = __import__
+ package = types.ModuleType('package')
+ package.__path__ = ['/a', '/b']
+ package.__spec__ = types.SimpleNamespace(
+ loader=None,
+ submodule_search_locations=['/a', '/b']
+ )
+
+ def _import(packagename, *args, **kwargs):
+ sys.modules[packagename] = package
+ return package
+
+ def cleanup():
+ builtins.__import__ = orig_import
+ self.addCleanup(cleanup)
+ builtins.__import__ = _import
+
+ _find_tests_args = []
+ def _find_tests(start_dir, pattern, namespace=None):
+ _find_tests_args.append((start_dir, pattern))
+ return ['%s/tests' % start_dir]
+
+ loader._find_tests = _find_tests
+ loader.suiteClass = list
+ suite = loader.discover('package')
+ self.assertEqual(suite, ['/a/tests', '/b/tests'])
+
+ def test_discovery_failed_discovery(self):
+ loader = unittest.TestLoader()
+ package = types.ModuleType('package')
+ orig_import = __import__
+
+ def _import(packagename, *args, **kwargs):
+ sys.modules[packagename] = package
+ return package
+
+ def cleanup():
+ builtins.__import__ = orig_import
+ self.addCleanup(cleanup)
+ builtins.__import__ = _import
+
+ with self.assertRaises(TypeError) as cm:
+ loader.discover('package')
+ self.assertEqual(str(cm.exception),
+ 'don\'t know how to discover from {!r}'
+ .format(package))
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/Lib/unittest/test/test_functiontestcase.py b/Lib/unittest/test/test_functiontestcase.py
index 9ce5ee3556e..d7fe07a53b9 100644
--- a/Lib/unittest/test/test_functiontestcase.py
+++ b/Lib/unittest/test/test_functiontestcase.py
@@ -142,3 +142,7 @@ class Test_FunctionTestCase(unittest.TestCase):
test = unittest.FunctionTestCase(lambda: None, description=desc)
self.assertEqual(test.shortDescription(), "this tests foo")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/unittest/test/test_loader.py b/Lib/unittest/test/test_loader.py
index fcd2e076245..b62a1b5c541 100644
--- a/Lib/unittest/test/test_loader.py
+++ b/Lib/unittest/test/test_loader.py
@@ -1306,3 +1306,7 @@ class Test_TestLoader(unittest.TestCase):
def test_suiteClass__default_value(self):
loader = unittest.TestLoader()
self.assertIs(loader.suiteClass, unittest.TestSuite)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/unittest/test/test_program.py b/Lib/unittest/test/test_program.py
index 8a4b3fad581..32942980306 100644
--- a/Lib/unittest/test/test_program.py
+++ b/Lib/unittest/test/test_program.py
@@ -2,6 +2,7 @@ import io
import os
import sys
+from test import support
import unittest
@@ -64,6 +65,41 @@ class Test_TestProgram(unittest.TestCase):
return self.suiteClass(
[self.loadTestsFromTestCase(Test_TestProgram.FooBar)])
+ def loadTestsFromNames(self, names, module):
+ return self.suiteClass(
+ [self.loadTestsFromTestCase(Test_TestProgram.FooBar)])
+
+ def test_defaultTest_with_string(self):
+ class FakeRunner(object):
+ def run(self, test):
+ self.test = test
+ return True
+
+ old_argv = sys.argv
+ sys.argv = ['faketest']
+ runner = FakeRunner()
+ program = unittest.TestProgram(testRunner=runner, exit=False,
+ defaultTest='unittest.test',
+ testLoader=self.FooBarLoader())
+ sys.argv = old_argv
+ self.assertEqual(('unittest.test',), program.testNames)
+
+ def test_defaultTest_with_iterable(self):
+ class FakeRunner(object):
+ def run(self, test):
+ self.test = test
+ return True
+
+ old_argv = sys.argv
+ sys.argv = ['faketest']
+ runner = FakeRunner()
+ program = unittest.TestProgram(
+ testRunner=runner, exit=False,
+ defaultTest=['unittest.test', 'unittest.test2'],
+ testLoader=self.FooBarLoader())
+ sys.argv = old_argv
+ self.assertEqual(['unittest.test', 'unittest.test2'],
+ program.testNames)
def test_NonExit(self):
program = unittest.main(exit=False,
@@ -151,20 +187,38 @@ class TestCommandLineArgs(unittest.TestCase):
if attr == 'catch' and not hasInstallHandler:
continue
+ setattr(program, attr, None)
+ program.parseArgs([None])
+ self.assertIs(getattr(program, attr), False)
+
+ false = []
+ setattr(program, attr, false)
+ program.parseArgs([None])
+ self.assertIs(getattr(program, attr), false)
+
+ true = [42]
+ setattr(program, attr, true)
+ program.parseArgs([None])
+ self.assertIs(getattr(program, attr), true)
+
short_opt = '-%s' % arg[0]
long_opt = '--%s' % arg
for opt in short_opt, long_opt:
setattr(program, attr, None)
-
- program.parseArgs([None, opt])
- self.assertTrue(getattr(program, attr))
-
- for opt in short_opt, long_opt:
- not_none = object()
- setattr(program, attr, not_none)
-
program.parseArgs([None, opt])
- self.assertEqual(getattr(program, attr), not_none)
+ self.assertIs(getattr(program, attr), True)
+
+ setattr(program, attr, False)
+ with support.captured_stderr() as stderr, \
+ self.assertRaises(SystemExit) as cm:
+ program.parseArgs([None, opt])
+ self.assertEqual(cm.exception.args, (2,))
+
+ setattr(program, attr, True)
+ with support.captured_stderr() as stderr, \
+ self.assertRaises(SystemExit) as cm:
+ program.parseArgs([None, opt])
+ self.assertEqual(cm.exception.args, (2,))
def testWarning(self):
"""Test the warnings argument"""
diff --git a/Lib/unittest/test/test_result.py b/Lib/unittest/test/test_result.py
index 7d40725cf2c..489fe177546 100644
--- a/Lib/unittest/test/test_result.py
+++ b/Lib/unittest/test/test_result.py
@@ -227,6 +227,40 @@ class Test_TestResult(unittest.TestCase):
self.assertIs(test_case, test)
self.assertIsInstance(formatted_exc, str)
+ def test_addSubTest(self):
+ class Foo(unittest.TestCase):
+ def test_1(self):
+ nonlocal subtest
+ with self.subTest(foo=1):
+ subtest = self._subtest
+ try:
+ 1/0
+ except ZeroDivisionError:
+ exc_info_tuple = sys.exc_info()
+ # Register an error by hand (to check the API)
+ result.addSubTest(test, subtest, exc_info_tuple)
+ # Now trigger a failure
+ self.fail("some recognizable failure")
+
+ subtest = None
+ test = Foo('test_1')
+ result = unittest.TestResult()
+
+ test.run(result)
+
+ self.assertFalse(result.wasSuccessful())
+ self.assertEqual(len(result.errors), 1)
+ self.assertEqual(len(result.failures), 1)
+ self.assertEqual(result.testsRun, 1)
+ self.assertEqual(result.shouldStop, False)
+
+ test_case, formatted_exc = result.errors[0]
+ self.assertIs(test_case, subtest)
+ self.assertIn("ZeroDivisionError", formatted_exc)
+ test_case, formatted_exc = result.failures[0]
+ self.assertIs(test_case, subtest)
+ self.assertIn("some recognizable failure", formatted_exc)
+
def testGetDescriptionWithoutDocstring(self):
result = unittest.TextTestResult(None, True, 1)
self.assertEqual(
@@ -234,6 +268,37 @@ class Test_TestResult(unittest.TestCase):
'testGetDescriptionWithoutDocstring (' + __name__ +
'.Test_TestResult)')
+ def testGetSubTestDescriptionWithoutDocstring(self):
+ with self.subTest(foo=1, bar=2):
+ result = unittest.TextTestResult(None, True, 1)
+ self.assertEqual(
+ result.getDescription(self._subtest),
+ 'testGetSubTestDescriptionWithoutDocstring (' + __name__ +
+ '.Test_TestResult) (bar=2, foo=1)')
+ with self.subTest('some message'):
+ result = unittest.TextTestResult(None, True, 1)
+ self.assertEqual(
+ result.getDescription(self._subtest),
+ 'testGetSubTestDescriptionWithoutDocstring (' + __name__ +
+ '.Test_TestResult) [some message]')
+
+ def testGetSubTestDescriptionWithoutDocstringAndParams(self):
+ with self.subTest():
+ result = unittest.TextTestResult(None, True, 1)
+ self.assertEqual(
+ result.getDescription(self._subtest),
+ 'testGetSubTestDescriptionWithoutDocstringAndParams '
+ '(' + __name__ + '.Test_TestResult) (<subtest>)')
+
+ def testGetNestedSubTestDescriptionWithoutDocstring(self):
+ with self.subTest(foo=1):
+ with self.subTest(bar=2):
+ result = unittest.TextTestResult(None, True, 1)
+ self.assertEqual(
+ result.getDescription(self._subtest),
+ 'testGetNestedSubTestDescriptionWithoutDocstring '
+ '(' + __name__ + '.Test_TestResult) (bar=2, foo=1)')
+
@unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")
def testGetDescriptionWithOneLineDocstring(self):
@@ -247,6 +312,18 @@ class Test_TestResult(unittest.TestCase):
@unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")
+ def testGetSubTestDescriptionWithOneLineDocstring(self):
+ """Tests getDescription() for a method with a docstring."""
+ result = unittest.TextTestResult(None, True, 1)
+ with self.subTest(foo=1, bar=2):
+ self.assertEqual(
+ result.getDescription(self._subtest),
+ ('testGetSubTestDescriptionWithOneLineDocstring '
+ '(' + __name__ + '.Test_TestResult) (bar=2, foo=1)\n'
+ 'Tests getDescription() for a method with a docstring.'))
+
+ @unittest.skipIf(sys.flags.optimize >= 2,
+ "Docstrings are omitted with -O2 and above")
def testGetDescriptionWithMultiLineDocstring(self):
"""Tests getDescription() for a method with a longer docstring.
The second line of the docstring.
@@ -259,6 +336,21 @@ class Test_TestResult(unittest.TestCase):
'Tests getDescription() for a method with a longer '
'docstring.'))
+ @unittest.skipIf(sys.flags.optimize >= 2,
+ "Docstrings are omitted with -O2 and above")
+ def testGetSubTestDescriptionWithMultiLineDocstring(self):
+ """Tests getDescription() for a method with a longer docstring.
+ The second line of the docstring.
+ """
+ result = unittest.TextTestResult(None, True, 1)
+ with self.subTest(foo=1, bar=2):
+ self.assertEqual(
+ result.getDescription(self._subtest),
+ ('testGetSubTestDescriptionWithMultiLineDocstring '
+ '(' + __name__ + '.Test_TestResult) (bar=2, foo=1)\n'
+ 'Tests getDescription() for a method with a longer '
+ 'docstring.'))
+
def testStackFrameTrimming(self):
class Frame(object):
class tb_frame(object):
diff --git a/Lib/unittest/test/test_runner.py b/Lib/unittest/test/test_runner.py
index e22e6bc279e..ef1c1af9f10 100644
--- a/Lib/unittest/test/test_runner.py
+++ b/Lib/unittest/test/test_runner.py
@@ -5,6 +5,7 @@ import pickle
import subprocess
import unittest
+from unittest.case import _Outcome
from .support import LoggingResult, ResultWithNoStartTestRunStopTestRun
@@ -42,12 +43,8 @@ class TestCleanUp(unittest.TestCase):
def testNothing(self):
pass
- class MockOutcome(object):
- success = True
- errors = []
-
test = TestableTest('testNothing')
- test._outcomeForDoCleanups = MockOutcome
+ outcome = test._outcome = _Outcome()
exc1 = Exception('foo')
exc2 = Exception('bar')
@@ -61,9 +58,10 @@ class TestCleanUp(unittest.TestCase):
test.addCleanup(cleanup2)
self.assertFalse(test.doCleanups())
- self.assertFalse(MockOutcome.success)
+ self.assertFalse(outcome.success)
- (Type1, instance1, _), (Type2, instance2, _) = reversed(MockOutcome.errors)
+ ((_, (Type1, instance1, _)),
+ (_, (Type2, instance2, _))) = reversed(outcome.errors)
self.assertEqual((Type1, instance1), (Exception, exc1))
self.assertEqual((Type2, instance2), (Exception, exc2))
@@ -341,3 +339,7 @@ class Test_TextTestRunner(unittest.TestCase):
f = io.StringIO()
runner = unittest.TextTestRunner(f)
self.assertTrue(runner.stream.stream is f)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/unittest/test/test_setups.py b/Lib/unittest/test/test_setups.py
index b8d5aa41e94..392f95efc07 100644
--- a/Lib/unittest/test/test_setups.py
+++ b/Lib/unittest/test/test_setups.py
@@ -494,14 +494,13 @@ class TestSetups(unittest.TestCase):
Test.__module__ = 'Module'
sys.modules['Module'] = Module
- _suite = unittest.defaultTestLoader.loadTestsFromTestCase(Test)
- suite = unittest.TestSuite()
- suite.addTest(_suite)
-
messages = ('setUpModule', 'tearDownModule', 'setUpClass', 'tearDownClass', 'test_something')
for phase, msg in enumerate(messages):
+ _suite = unittest.defaultTestLoader.loadTestsFromTestCase(Test)
+ suite = unittest.TestSuite([_suite])
with self.assertRaisesRegex(Exception, msg):
suite.debug()
+
if __name__ == '__main__':
unittest.main()
diff --git a/Lib/unittest/test/test_skipping.py b/Lib/unittest/test/test_skipping.py
index 952240eeede..314a7a4d0f8 100644
--- a/Lib/unittest/test/test_skipping.py
+++ b/Lib/unittest/test/test_skipping.py
@@ -29,6 +29,31 @@ class Test_TestSkipping(unittest.TestCase):
self.assertEqual(result.skipped, [(test, "testing")])
self.assertEqual(result.testsRun, 1)
+ def test_skipping_subtests(self):
+ class Foo(unittest.TestCase):
+ def test_skip_me(self):
+ with self.subTest(a=1):
+ with self.subTest(b=2):
+ self.skipTest("skip 1")
+ self.skipTest("skip 2")
+ self.skipTest("skip 3")
+ events = []
+ result = LoggingResult(events)
+ test = Foo("test_skip_me")
+ test.run(result)
+ self.assertEqual(events, ['startTest', 'addSkip', 'addSkip',
+ 'addSkip', 'stopTest'])
+ self.assertEqual(len(result.skipped), 3)
+ subtest, msg = result.skipped[0]
+ self.assertEqual(msg, "skip 1")
+ self.assertIsInstance(subtest, unittest.TestCase)
+ self.assertIsNot(subtest, test)
+ subtest, msg = result.skipped[1]
+ self.assertEqual(msg, "skip 2")
+ self.assertIsInstance(subtest, unittest.TestCase)
+ self.assertIsNot(subtest, test)
+ self.assertEqual(result.skipped[2], (test, "skip 3"))
+
def test_skipping_decorators(self):
op_table = ((unittest.skipUnless, False, True),
(unittest.skipIf, True, False))
@@ -95,6 +120,31 @@ class Test_TestSkipping(unittest.TestCase):
self.assertEqual(result.expectedFailures[0][0], test)
self.assertTrue(result.wasSuccessful())
+ def test_expected_failure_subtests(self):
+ # A failure in any subtest counts as the expected failure of the
+ # whole test.
+ class Foo(unittest.TestCase):
+ @unittest.expectedFailure
+ def test_die(self):
+ with self.subTest():
+ # This one succeeds
+ pass
+ with self.subTest():
+ self.fail("help me!")
+ with self.subTest():
+ # This one doesn't get executed
+ self.fail("shouldn't come here")
+ events = []
+ result = LoggingResult(events)
+ test = Foo("test_die")
+ test.run(result)
+ self.assertEqual(events,
+ ['startTest', 'addSubTestSuccess',
+ 'addExpectedFailure', 'stopTest'])
+ self.assertEqual(len(result.expectedFailures), 1)
+ self.assertIs(result.expectedFailures[0][0], test)
+ self.assertTrue(result.wasSuccessful())
+
def test_unexpected_success(self):
class Foo(unittest.TestCase):
@unittest.expectedFailure
@@ -108,7 +158,31 @@ class Test_TestSkipping(unittest.TestCase):
['startTest', 'addUnexpectedSuccess', 'stopTest'])
self.assertFalse(result.failures)
self.assertEqual(result.unexpectedSuccesses, [test])
- self.assertTrue(result.wasSuccessful())
+ self.assertFalse(result.wasSuccessful())
+
+ def test_unexpected_success_subtests(self):
+ # Success in all subtests counts as the unexpected success of
+ # the whole test.
+ class Foo(unittest.TestCase):
+ @unittest.expectedFailure
+ def test_die(self):
+ with self.subTest():
+ # This one succeeds
+ pass
+ with self.subTest():
+ # So does this one
+ pass
+ events = []
+ result = LoggingResult(events)
+ test = Foo("test_die")
+ test.run(result)
+ self.assertEqual(events,
+ ['startTest',
+ 'addSubTestSuccess', 'addSubTestSuccess',
+ 'addUnexpectedSuccess', 'stopTest'])
+ self.assertFalse(result.failures)
+ self.assertEqual(result.unexpectedSuccesses, [test])
+ self.assertFalse(result.wasSuccessful())
def test_skip_doesnt_run_setup(self):
class Foo(unittest.TestCase):
@@ -147,3 +221,7 @@ class Test_TestSkipping(unittest.TestCase):
suite = unittest.TestSuite([test])
suite.run(result)
self.assertEqual(result.skipped, [(test, "testing")])
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/unittest/test/test_suite.py b/Lib/unittest/test/test_suite.py
index 2db978ddb8a..38311ca4880 100644
--- a/Lib/unittest/test/test_suite.py
+++ b/Lib/unittest/test/test_suite.py
@@ -1,6 +1,8 @@
import unittest
+import gc
import sys
+import weakref
from .support import LoggingResult, TestEquality
@@ -49,6 +51,9 @@ class Test_TestSuite(unittest.TestCase, TestEquality):
suite = unittest.TestSuite()
self.assertEqual(suite.countTestCases(), 0)
+ # countTestCases() still works after tests are run
+ suite.run(unittest.TestResult())
+ self.assertEqual(suite.countTestCases(), 0)
# "class TestSuite([tests])"
# ...
@@ -61,6 +66,9 @@ class Test_TestSuite(unittest.TestCase, TestEquality):
suite = unittest.TestSuite([])
self.assertEqual(suite.countTestCases(), 0)
+ # countTestCases() still works after tests are run
+ suite.run(unittest.TestResult())
+ self.assertEqual(suite.countTestCases(), 0)
# "class TestSuite([tests])"
# ...
@@ -82,6 +90,14 @@ class Test_TestSuite(unittest.TestCase, TestEquality):
suite_3 = unittest.TestSuite(set(suite_1))
self.assertEqual(suite_3.countTestCases(), 2)
+ # countTestCases() still works after tests are run
+ suite_1.run(unittest.TestResult())
+ self.assertEqual(suite_1.countTestCases(), 2)
+ suite_2.run(unittest.TestResult())
+ self.assertEqual(suite_2.countTestCases(), 2)
+ suite_3.run(unittest.TestResult())
+ self.assertEqual(suite_3.countTestCases(), 2)
+
# "class TestSuite([tests])"
# ...
# "If tests is given, it must be an iterable of individual test cases
@@ -97,6 +113,9 @@ class Test_TestSuite(unittest.TestCase, TestEquality):
suite = unittest.TestSuite(tests())
self.assertEqual(suite.countTestCases(), 2)
+ # countTestCases() still works after tests are run
+ suite.run(unittest.TestResult())
+ self.assertEqual(suite.countTestCases(), 2)
################################################################
### /Tests for TestSuite.__init__
@@ -143,6 +162,9 @@ class Test_TestSuite(unittest.TestCase, TestEquality):
suite = unittest.TestSuite((test1, test2))
self.assertEqual(suite.countTestCases(), 2)
+ # countTestCases() still works after tests are run
+ suite.run(unittest.TestResult())
+ self.assertEqual(suite.countTestCases(), 2)
# "Return the number of tests represented by the this test object.
# ...this method is also implemented by the TestSuite class, which can
@@ -160,6 +182,10 @@ class Test_TestSuite(unittest.TestCase, TestEquality):
parent = unittest.TestSuite((test3, child, Test1('test1')))
self.assertEqual(parent.countTestCases(), 4)
+ # countTestCases() still works after tests are run
+ parent.run(unittest.TestResult())
+ self.assertEqual(parent.countTestCases(), 4)
+ self.assertEqual(child.countTestCases(), 2)
# "Run the tests associated with this suite, collecting the result into
# the test result object passed as result."
@@ -218,6 +244,9 @@ class Test_TestSuite(unittest.TestCase, TestEquality):
self.assertEqual(suite.countTestCases(), 1)
self.assertEqual(list(suite), [test])
+ # countTestCases() still works after tests are run
+ suite.run(unittest.TestResult())
+ self.assertEqual(suite.countTestCases(), 1)
# "Add a ... TestSuite to the suite"
def test_addTest__TestSuite(self):
@@ -231,6 +260,9 @@ class Test_TestSuite(unittest.TestCase, TestEquality):
self.assertEqual(suite.countTestCases(), 1)
self.assertEqual(list(suite), [suite_2])
+ # countTestCases() still works after tests are run
+ suite.run(unittest.TestResult())
+ self.assertEqual(suite.countTestCases(), 1)
# "Add all the tests from an iterable of TestCase and TestSuite
# instances to this test suite."
@@ -300,7 +332,54 @@ class Test_TestSuite(unittest.TestCase, TestEquality):
# when the bug is fixed this line will not crash
suite.run(unittest.TestResult())
+ def test_remove_test_at_index(self):
+ if not unittest.BaseTestSuite._cleanup:
+ raise unittest.SkipTest("Suite cleanup is disabled")
+
+ suite = unittest.TestSuite()
+
+ suite._tests = [1, 2, 3]
+ suite._removeTestAtIndex(1)
+
+ self.assertEqual([1, None, 3], suite._tests)
+
+ def test_remove_test_at_index_not_indexable(self):
+ if not unittest.BaseTestSuite._cleanup:
+ raise unittest.SkipTest("Suite cleanup is disabled")
+
+ suite = unittest.TestSuite()
+ suite._tests = None
+
+ # if _removeAtIndex raises for noniterables this next line will break
+ suite._removeTestAtIndex(2)
+
+ def assert_garbage_collect_test_after_run(self, TestSuiteClass):
+ if not unittest.BaseTestSuite._cleanup:
+ raise unittest.SkipTest("Suite cleanup is disabled")
+
+ class Foo(unittest.TestCase):
+ def test_nothing(self):
+ pass
+
+ test = Foo('test_nothing')
+ wref = weakref.ref(test)
+
+ suite = TestSuiteClass([wref()])
+ suite.run(unittest.TestResult())
+
+ del test
+
+ # for the benefit of non-reference counting implementations
+ gc.collect()
+ self.assertEqual(suite._tests, [None])
+ self.assertIsNone(wref())
+
+ def test_garbage_collect_test_after_run_BaseTestSuite(self):
+ self.assert_garbage_collect_test_after_run(unittest.BaseTestSuite)
+
+ def test_garbage_collect_test_after_run_TestSuite(self):
+ self.assert_garbage_collect_test_after_run(unittest.TestSuite)
def test_basetestsuite(self):
class Test(unittest.TestCase):
@@ -343,6 +422,7 @@ class Test_TestSuite(unittest.TestCase, TestEquality):
self.assertEqual(len(result.errors), 1)
self.assertEqual(len(result.failures), 0)
self.assertEqual(result.testsRun, 2)
+ self.assertEqual(suite.countTestCases(), 2)
def test_overriding_call(self):
@@ -363,6 +443,5 @@ class Test_TestSuite(unittest.TestCase, TestEquality):
self.assertFalse(result._testRunEntered)
-
if __name__ == '__main__':
unittest.main()
diff --git a/Lib/unittest/test/testmock/__main__.py b/Lib/unittest/test/testmock/__main__.py
new file mode 100644
index 00000000000..45c633a4ee4
--- /dev/null
+++ b/Lib/unittest/test/testmock/__main__.py
@@ -0,0 +1,18 @@
+import os
+import unittest
+
+
+def load_tests(loader, standard_tests, pattern):
+ # top level directory cached on loader instance
+ this_dir = os.path.dirname(__file__)
+ pattern = pattern or "test*.py"
+ # We are inside unittest.test.testmock, so the top-level is three notches up
+ top_level_dir = os.path.dirname(os.path.dirname(os.path.dirname(this_dir)))
+ package_tests = loader.discover(start_dir=this_dir, pattern=pattern,
+ top_level_dir=top_level_dir)
+ standard_tests.addTests(package_tests)
+ return standard_tests
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Lib/unittest/test/testmock/testcallable.py b/Lib/unittest/test/testmock/testcallable.py
index 7b2dd003ea2..5390a4e10f3 100644
--- a/Lib/unittest/test/testmock/testcallable.py
+++ b/Lib/unittest/test/testmock/testcallable.py
@@ -145,3 +145,7 @@ class TestCallable(unittest.TestCase):
mock.wibble.assert_called_once_with()
self.assertRaises(TypeError, mock.wibble, 'some', 'args')
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py
index a362a2f7848..1dbc0b64ba5 100644
--- a/Lib/unittest/test/testmock/testhelpers.py
+++ b/Lib/unittest/test/testmock/testhelpers.py
@@ -337,9 +337,10 @@ class SpecSignatureTest(unittest.TestCase):
def test_basic(self):
- for spec in (SomeClass, SomeClass()):
- mock = create_autospec(spec)
- self._check_someclass_mock(mock)
+ mock = create_autospec(SomeClass)
+ self._check_someclass_mock(mock)
+ mock = create_autospec(SomeClass())
+ self._check_someclass_mock(mock)
def test_create_autospec_return_value(self):
@@ -576,10 +577,10 @@ class SpecSignatureTest(unittest.TestCase):
def test_spec_inheritance_for_classes(self):
class Foo(object):
- def a(self):
+ def a(self, x):
pass
class Bar(object):
- def f(self):
+ def f(self, y):
pass
class_mock = create_autospec(Foo)
@@ -587,26 +588,30 @@ class SpecSignatureTest(unittest.TestCase):
self.assertIsNot(class_mock, class_mock())
for this_mock in class_mock, class_mock():
- this_mock.a()
- this_mock.a.assert_called_with()
- self.assertRaises(TypeError, this_mock.a, 'foo')
+ this_mock.a(x=5)
+ this_mock.a.assert_called_with(x=5)
+ this_mock.a.assert_called_with(5)
+ self.assertRaises(TypeError, this_mock.a, 'foo', 'bar')
self.assertRaises(AttributeError, getattr, this_mock, 'b')
instance_mock = create_autospec(Foo())
- instance_mock.a()
- instance_mock.a.assert_called_with()
- self.assertRaises(TypeError, instance_mock.a, 'foo')
+ instance_mock.a(5)
+ instance_mock.a.assert_called_with(5)
+ instance_mock.a.assert_called_with(x=5)
+ self.assertRaises(TypeError, instance_mock.a, 'foo', 'bar')
self.assertRaises(AttributeError, getattr, instance_mock, 'b')
# The return value isn't isn't callable
self.assertRaises(TypeError, instance_mock)
- instance_mock.Bar.f()
- instance_mock.Bar.f.assert_called_with()
+ instance_mock.Bar.f(6)
+ instance_mock.Bar.f.assert_called_with(6)
+ instance_mock.Bar.f.assert_called_with(y=6)
self.assertRaises(AttributeError, getattr, instance_mock.Bar, 'g')
- instance_mock.Bar().f()
- instance_mock.Bar().f.assert_called_with()
+ instance_mock.Bar().f(6)
+ instance_mock.Bar().f.assert_called_with(6)
+ instance_mock.Bar().f.assert_called_with(y=6)
self.assertRaises(AttributeError, getattr, instance_mock.Bar(), 'g')
@@ -663,12 +668,15 @@ class SpecSignatureTest(unittest.TestCase):
self.assertRaises(TypeError, mock)
mock(1, 2)
mock.assert_called_with(1, 2)
+ mock.assert_called_with(1, b=2)
+ mock.assert_called_with(a=1, b=2)
f.f = f
mock = create_autospec(f)
self.assertRaises(TypeError, mock.f)
mock.f(3, 4)
mock.f.assert_called_with(3, 4)
+ mock.f.assert_called_with(a=3, b=4)
def test_skip_attributeerrors(self):
@@ -704,9 +712,13 @@ class SpecSignatureTest(unittest.TestCase):
self.assertRaises(TypeError, mock)
mock(1)
mock.assert_called_once_with(1)
+ mock.assert_called_once_with(a=1)
+ self.assertRaises(AssertionError, mock.assert_called_once_with, 2)
mock(4, 5)
mock.assert_called_with(4, 5)
+ mock.assert_called_with(a=4, b=5)
+ self.assertRaises(AssertionError, mock.assert_called_with, a=5, b=4)
def test_class_with_no_init(self):
@@ -719,24 +731,27 @@ class SpecSignatureTest(unittest.TestCase):
def test_signature_callable(self):
class Callable(object):
- def __init__(self):
+ def __init__(self, x, y):
pass
def __call__(self, a):
pass
mock = create_autospec(Callable)
- mock()
- mock.assert_called_once_with()
+ mock(1, 2)
+ mock.assert_called_once_with(1, 2)
+ mock.assert_called_once_with(x=1, y=2)
self.assertRaises(TypeError, mock, 'a')
- instance = mock()
+ instance = mock(1, 2)
self.assertRaises(TypeError, instance)
instance(a='a')
+ instance.assert_called_once_with('a')
instance.assert_called_once_with(a='a')
instance('a')
instance.assert_called_with('a')
+ instance.assert_called_with(a='a')
- mock = create_autospec(Callable())
+ mock = create_autospec(Callable(1, 2))
mock(a='a')
mock.assert_called_once_with(a='a')
self.assertRaises(TypeError, mock)
@@ -779,7 +794,11 @@ class SpecSignatureTest(unittest.TestCase):
pass
a = create_autospec(Foo)
+ a.f(10)
+ a.f.assert_called_with(10)
+ a.f.assert_called_with(self=10)
a.f(self=10)
+ a.f.assert_called_with(10)
a.f.assert_called_with(self=10)
diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py
index cef5405fe95..20cc6541e66 100644
--- a/Lib/unittest/test/testmock/testmock.py
+++ b/Lib/unittest/test/testmock/testmock.py
@@ -25,6 +25,18 @@ class Iter(object):
__next__ = next
+class Something(object):
+ def meth(self, a, b, c, d=None):
+ pass
+
+ @classmethod
+ def cmeth(cls, a, b, c, d=None):
+ pass
+
+ @staticmethod
+ def smeth(a, b, c, d=None):
+ pass
+
class MockTest(unittest.TestCase):
@@ -273,6 +285,43 @@ class MockTest(unittest.TestCase):
mock.assert_called_with(1, 2, 3, a='fish', b='nothing')
+ def test_assert_called_with_function_spec(self):
+ def f(a, b, c, d=None):
+ pass
+
+ mock = Mock(spec=f)
+
+ mock(1, b=2, c=3)
+ mock.assert_called_with(1, 2, 3)
+ mock.assert_called_with(a=1, b=2, c=3)
+ self.assertRaises(AssertionError, mock.assert_called_with,
+ 1, b=3, c=2)
+ # Expected call doesn't match the spec's signature
+ with self.assertRaises(AssertionError) as cm:
+ mock.assert_called_with(e=8)
+ self.assertIsInstance(cm.exception.__cause__, TypeError)
+
+
+ def test_assert_called_with_method_spec(self):
+ def _check(mock):
+ mock(1, b=2, c=3)
+ mock.assert_called_with(1, 2, 3)
+ mock.assert_called_with(a=1, b=2, c=3)
+ self.assertRaises(AssertionError, mock.assert_called_with,
+ 1, b=3, c=2)
+
+ mock = Mock(spec=Something().meth)
+ _check(mock)
+ mock = Mock(spec=Something.cmeth)
+ _check(mock)
+ mock = Mock(spec=Something().cmeth)
+ _check(mock)
+ mock = Mock(spec=Something.smeth)
+ _check(mock)
+ mock = Mock(spec=Something().smeth)
+ _check(mock)
+
+
def test_assert_called_once_with(self):
mock = Mock()
mock()
@@ -297,6 +346,29 @@ class MockTest(unittest.TestCase):
)
+ def test_assert_called_once_with_function_spec(self):
+ def f(a, b, c, d=None):
+ pass
+
+ mock = Mock(spec=f)
+
+ mock(1, b=2, c=3)
+ mock.assert_called_once_with(1, 2, 3)
+ mock.assert_called_once_with(a=1, b=2, c=3)
+ self.assertRaises(AssertionError, mock.assert_called_once_with,
+ 1, b=3, c=2)
+ # Expected call doesn't match the spec's signature
+ with self.assertRaises(AssertionError) as cm:
+ mock.assert_called_once_with(e=8)
+ self.assertIsInstance(cm.exception.__cause__, TypeError)
+ # Mock called more than once => always fails
+ mock(4, 5, 6)
+ self.assertRaises(AssertionError, mock.assert_called_once_with,
+ 1, 2, 3)
+ self.assertRaises(AssertionError, mock.assert_called_once_with,
+ 4, 5, 6)
+
+
def test_attribute_access_returns_mocks(self):
mock = Mock()
something = mock.something
@@ -995,6 +1067,39 @@ class MockTest(unittest.TestCase):
)
+ def test_assert_has_calls_with_function_spec(self):
+ def f(a, b, c, d=None):
+ pass
+
+ mock = Mock(spec=f)
+
+ mock(1, b=2, c=3)
+ mock(4, 5, c=6, d=7)
+ mock(10, 11, c=12)
+ calls = [
+ ('', (1, 2, 3), {}),
+ ('', (4, 5, 6), {'d': 7}),
+ ((10, 11, 12), {}),
+ ]
+ mock.assert_has_calls(calls)
+ mock.assert_has_calls(calls, any_order=True)
+ mock.assert_has_calls(calls[1:])
+ mock.assert_has_calls(calls[1:], any_order=True)
+ mock.assert_has_calls(calls[:-1])
+ mock.assert_has_calls(calls[:-1], any_order=True)
+ # Reversed order
+ calls = list(reversed(calls))
+ with self.assertRaises(AssertionError):
+ mock.assert_has_calls(calls)
+ mock.assert_has_calls(calls, any_order=True)
+ with self.assertRaises(AssertionError):
+ mock.assert_has_calls(calls[1:])
+ mock.assert_has_calls(calls[1:], any_order=True)
+ with self.assertRaises(AssertionError):
+ mock.assert_has_calls(calls[:-1])
+ mock.assert_has_calls(calls[:-1], any_order=True)
+
+
def test_assert_any_call(self):
mock = Mock()
mock(1, 2)
@@ -1021,6 +1126,26 @@ class MockTest(unittest.TestCase):
)
+ def test_assert_any_call_with_function_spec(self):
+ def f(a, b, c, d=None):
+ pass
+
+ mock = Mock(spec=f)
+
+ mock(1, b=2, c=3)
+ mock(4, 5, c=6, d=7)
+ mock.assert_any_call(1, 2, 3)
+ mock.assert_any_call(a=1, b=2, c=3)
+ mock.assert_any_call(4, 5, 6, 7)
+ mock.assert_any_call(a=4, b=5, c=6, d=7)
+ self.assertRaises(AssertionError, mock.assert_any_call,
+ 1, b=3, c=2)
+ # Expected call doesn't match the spec's signature
+ with self.assertRaises(AssertionError) as cm:
+ mock.assert_any_call(e=8)
+ self.assertIsInstance(cm.exception.__cause__, TypeError)
+
+
def test_mock_calls_create_autospec(self):
def f(a, b):
pass
@@ -1177,20 +1302,6 @@ class MockTest(unittest.TestCase):
self.assertEqual(m.method_calls, [])
- def test_attribute_deletion(self):
- # this behaviour isn't *useful*, but at least it's now tested...
- for Klass in Mock, MagicMock, NonCallableMagicMock, NonCallableMock:
- m = Klass()
- original = m.foo
- m.foo = 3
- del m.foo
- self.assertEqual(m.foo, original)
-
- new = m.foo = Mock()
- del m.foo
- self.assertEqual(m.foo, new)
-
-
def test_mock_parents(self):
for Klass in Mock, MagicMock:
m = Klass()
@@ -1254,7 +1365,8 @@ class MockTest(unittest.TestCase):
def test_attribute_deletion(self):
- for mock in Mock(), MagicMock():
+ for mock in (Mock(), MagicMock(), NonCallableMagicMock(),
+ NonCallableMock()):
self.assertTrue(hasattr(mock, 'm'))
del mock.m
@@ -1274,6 +1386,5 @@ class MockTest(unittest.TestCase):
mock.foo
-
if __name__ == '__main__':
unittest.main()
diff --git a/Lib/unittest/test/testmock/testpatch.py b/Lib/unittest/test/testmock/testpatch.py
index c1091b4e9b3..c1bc34fa8c9 100644
--- a/Lib/unittest/test/testmock/testpatch.py
+++ b/Lib/unittest/test/testmock/testpatch.py
@@ -1780,6 +1780,5 @@ class PatchTest(unittest.TestCase):
self.assertIs(os.path, path)
-
if __name__ == '__main__':
unittest.main()
diff --git a/Lib/unittest/test/testmock/testwith.py b/Lib/unittest/test/testmock/testwith.py
index 0a0cfad120a..f54e051e945 100644
--- a/Lib/unittest/test/testmock/testwith.py
+++ b/Lib/unittest/test/testmock/testwith.py
@@ -172,5 +172,88 @@ class TestMockOpen(unittest.TestCase):
self.assertEqual(result, 'foo')
+ def test_readline_data(self):
+ # Check that readline will return all the lines from the fake file
+ mock = mock_open(read_data='foo\nbar\nbaz\n')
+ with patch('%s.open' % __name__, mock, create=True):
+ h = open('bar')
+ line1 = h.readline()
+ line2 = h.readline()
+ line3 = h.readline()
+ self.assertEqual(line1, 'foo\n')
+ self.assertEqual(line2, 'bar\n')
+ self.assertEqual(line3, 'baz\n')
+
+ # Check that we properly emulate a file that doesn't end in a newline
+ mock = mock_open(read_data='foo')
+ with patch('%s.open' % __name__, mock, create=True):
+ h = open('bar')
+ result = h.readline()
+ self.assertEqual(result, 'foo')
+
+
+ def test_readlines_data(self):
+ # Test that emulating a file that ends in a newline character works
+ mock = mock_open(read_data='foo\nbar\nbaz\n')
+ with patch('%s.open' % __name__, mock, create=True):
+ h = open('bar')
+ result = h.readlines()
+ self.assertEqual(result, ['foo\n', 'bar\n', 'baz\n'])
+
+ # Test that files without a final newline will also be correctly
+ # emulated
+ mock = mock_open(read_data='foo\nbar\nbaz')
+ with patch('%s.open' % __name__, mock, create=True):
+ h = open('bar')
+ result = h.readlines()
+
+ self.assertEqual(result, ['foo\n', 'bar\n', 'baz'])
+
+
+ def test_mock_open_read_with_argument(self):
+ # At one point calling read with an argument was broken
+ # for mocks returned by mock_open
+ some_data = 'foo\nbar\nbaz'
+ mock = mock_open(read_data=some_data)
+ self.assertEqual(mock().read(10), some_data)
+
+
+ def test_interleaved_reads(self):
+ # Test that calling read, readline, and readlines pulls data
+ # sequentially from the data we preload with
+ mock = mock_open(read_data='foo\nbar\nbaz\n')
+ with patch('%s.open' % __name__, mock, create=True):
+ h = open('bar')
+ line1 = h.readline()
+ rest = h.readlines()
+ self.assertEqual(line1, 'foo\n')
+ self.assertEqual(rest, ['bar\n', 'baz\n'])
+
+ mock = mock_open(read_data='foo\nbar\nbaz\n')
+ with patch('%s.open' % __name__, mock, create=True):
+ h = open('bar')
+ line1 = h.readline()
+ rest = h.read()
+ self.assertEqual(line1, 'foo\n')
+ self.assertEqual(rest, 'bar\nbaz\n')
+
+
+ def test_overriding_return_values(self):
+ mock = mock_open(read_data='foo')
+ handle = mock()
+
+ handle.read.return_value = 'bar'
+ handle.readline.return_value = 'bar'
+ handle.readlines.return_value = ['bar']
+
+ self.assertEqual(handle.read(), 'bar')
+ self.assertEqual(handle.readline(), 'bar')
+ self.assertEqual(handle.readlines(), ['bar'])
+
+ # call repeatedly to check that a StopIteration is not propagated
+ self.assertEqual(handle.readline(), 'bar')
+ self.assertEqual(handle.readline(), 'bar')
+
+
if __name__ == '__main__':
unittest.main()