summaryrefslogtreecommitdiff
path: root/Lib/unittest/test
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/unittest/test')
-rw-r--r--Lib/unittest/test/support.py4
-rw-r--r--Lib/unittest/test/test_assertions.py15
-rw-r--r--Lib/unittest/test/test_break.py3
-rw-r--r--Lib/unittest/test/test_case.py113
-rw-r--r--Lib/unittest/test/test_discovery.py320
-rw-r--r--Lib/unittest/test/test_loader.py423
-rw-r--r--Lib/unittest/test/test_program.py26
-rw-r--r--Lib/unittest/test/test_result.py43
-rw-r--r--Lib/unittest/test/test_runner.py10
-rw-r--r--Lib/unittest/test/test_setups.py7
-rw-r--r--Lib/unittest/test/testmock/testmagicmethods.py11
-rw-r--r--Lib/unittest/test/testmock/testmock.py45
-rw-r--r--Lib/unittest/test/testmock/testpatch.py24
13 files changed, 893 insertions, 151 deletions
diff --git a/Lib/unittest/test/support.py b/Lib/unittest/test/support.py
index 02e8f3a00b..529265304f 100644
--- a/Lib/unittest/test/support.py
+++ b/Lib/unittest/test/support.py
@@ -25,8 +25,6 @@ class TestHashing(object):
try:
if not hash(obj_1) == hash(obj_2):
self.fail("%r and %r do not hash equal" % (obj_1, obj_2))
- except KeyboardInterrupt:
- raise
except Exception as e:
self.fail("Problem hashing %r and %r: %s" % (obj_1, obj_2, e))
@@ -35,8 +33,6 @@ class TestHashing(object):
if hash(obj_1) == hash(obj_2):
self.fail("%s and %s hash equal, but shouldn't" %
(obj_1, obj_2))
- except KeyboardInterrupt:
- raise
except Exception as e:
self.fail("Problem hashing %s and %s: %s" % (obj_1, obj_2, e))
diff --git a/Lib/unittest/test/test_assertions.py b/Lib/unittest/test/test_assertions.py
index c349a95794..e6e2bc2ca7 100644
--- a/Lib/unittest/test/test_assertions.py
+++ b/Lib/unittest/test/test_assertions.py
@@ -133,7 +133,6 @@ class Test_Assertions(unittest.TestCase):
try:
self.assertNotRegex('Ala ma kota', r'k.t', 'Message')
except self.failureException as e:
- self.assertIn("'kot'", e.args[0])
self.assertIn('Message', e.args[0])
else:
self.fail('assertNotRegex should have failed.')
@@ -329,6 +328,20 @@ class TestLongMessage(unittest.TestCase):
"^unexpectedly identical: None$",
"^unexpectedly identical: None : oops$"])
+ def testAssertRegex(self):
+ self.assertMessages('assertRegex', ('foo', 'bar'),
+ ["^Regex didn't match:",
+ "^oops$",
+ "^Regex didn't match:",
+ "^Regex didn't match: (.*) : oops$"])
+
+ def testAssertNotRegex(self):
+ self.assertMessages('assertNotRegex', ('foo', 'foo'),
+ ["^Regex matched:",
+ "^oops$",
+ "^Regex matched:",
+ "^Regex matched: (.*) : oops$"])
+
def assertMessagesCM(self, methodName, args, func, errors):
"""
diff --git a/Lib/unittest/test/test_break.py b/Lib/unittest/test/test_break.py
index 0bf1a229b8..2c7501952c 100644
--- a/Lib/unittest/test/test_break.py
+++ b/Lib/unittest/test/test_break.py
@@ -211,6 +211,7 @@ class TestBreak(unittest.TestCase):
self.verbosity = verbosity
self.failfast = failfast
self.catchbreak = catchbreak
+ self.tb_locals = False
self.testRunner = FakeRunner
self.test = test
self.result = None
@@ -221,6 +222,7 @@ class TestBreak(unittest.TestCase):
self.assertEqual(FakeRunner.initArgs, [((), {'buffer': None,
'verbosity': verbosity,
'failfast': failfast,
+ 'tb_locals': False,
'warnings': None})])
self.assertEqual(FakeRunner.runArgs, [test])
self.assertEqual(p.result, result)
@@ -235,6 +237,7 @@ class TestBreak(unittest.TestCase):
self.assertEqual(FakeRunner.initArgs, [((), {'buffer': None,
'verbosity': verbosity,
'failfast': failfast,
+ 'tb_locals': False,
'warnings': None})])
self.assertEqual(FakeRunner.runArgs, [test])
self.assertEqual(p.result, result)
diff --git a/Lib/unittest/test/test_case.py b/Lib/unittest/test/test_case.py
index 321d67a82f..ada733b1ff 100644
--- a/Lib/unittest/test/test_case.py
+++ b/Lib/unittest/test/test_case.py
@@ -1103,12 +1103,9 @@ test case
except self.failureException as e:
# need to remove the first line of the error message
error = str(e).split('\n', 1)[1]
+ self.assertEqual(sample_text_error, error)
- # no fair testing ourself with ourself, and assertEqual is used for strings
- # so can't use assertEqual either. Just use assertTrue.
- self.assertTrue(sample_text_error == error)
-
- def testAsertEqualSingleLine(self):
+ def testAssertEqualSingleLine(self):
sample_text = "laden swallows fly slowly"
revised_sample_text = "unladen swallows fly quickly"
sample_text_error = """\
@@ -1120,8 +1117,9 @@ test case
try:
self.assertEqual(sample_text, revised_sample_text)
except self.failureException as e:
+ # need to remove the first line of the error message
error = str(e).split('\n', 1)[1]
- self.assertTrue(sample_text_error == error)
+ self.assertEqual(sample_text_error, error)
def testAssertIsNone(self):
self.assertIsNone(None)
@@ -1147,6 +1145,9 @@ test case
# Failure when no exception is raised
with self.assertRaises(self.failureException):
self.assertRaises(ExceptionMock, lambda: 0)
+ # Failure when the function is None
+ with self.assertWarns(DeprecationWarning):
+ self.assertRaises(ExceptionMock, None)
# Failure when another exception is raised
with self.assertRaises(ExceptionMock):
self.assertRaises(ValueError, Stub)
@@ -1171,10 +1172,31 @@ test case
with self.assertRaises(self.failureException):
with self.assertRaises(ExceptionMock):
pass
+ # Custom message
+ with self.assertRaisesRegex(self.failureException, 'foobar'):
+ with self.assertRaises(ExceptionMock, msg='foobar'):
+ pass
+ # Invalid keyword argument
+ with self.assertWarnsRegex(DeprecationWarning, 'foobar'), \
+ self.assertRaises(AssertionError):
+ with self.assertRaises(ExceptionMock, foobar=42):
+ pass
# Failure when another exception is raised
with self.assertRaises(ExceptionMock):
self.assertRaises(ValueError, Stub)
+ def testAssertRaisesNoExceptionType(self):
+ with self.assertRaises(TypeError):
+ self.assertRaises()
+ with self.assertRaises(TypeError):
+ self.assertRaises(1)
+ with self.assertRaises(TypeError):
+ self.assertRaises(object)
+ with self.assertRaises(TypeError):
+ self.assertRaises((ValueError, 1))
+ with self.assertRaises(TypeError):
+ self.assertRaises((ValueError, object))
+
def testAssertRaisesRegex(self):
class ExceptionMock(Exception):
pass
@@ -1184,6 +1206,8 @@ test case
self.assertRaisesRegex(ExceptionMock, re.compile('expect$'), Stub)
self.assertRaisesRegex(ExceptionMock, 'expect$', Stub)
+ with self.assertWarns(DeprecationWarning):
+ self.assertRaisesRegex(ExceptionMock, 'expect$', None)
def testAssertNotRaisesRegex(self):
self.assertRaisesRegex(
@@ -1194,6 +1218,15 @@ test case
self.failureException, '^Exception not raised by <lambda>$',
self.assertRaisesRegex, Exception, 'x',
lambda: None)
+ # Custom message
+ with self.assertRaisesRegex(self.failureException, 'foobar'):
+ with self.assertRaisesRegex(Exception, 'expect', msg='foobar'):
+ pass
+ # Invalid keyword argument
+ with self.assertWarnsRegex(DeprecationWarning, 'foobar'), \
+ self.assertRaises(AssertionError):
+ with self.assertRaisesRegex(Exception, 'expect', foobar=42):
+ pass
def testAssertRaisesRegexInvalidRegex(self):
# Issue 20145.
@@ -1237,6 +1270,20 @@ test case
self.assertIsInstance(e, ExceptionMock)
self.assertEqual(e.args[0], v)
+ def testAssertRaisesRegexNoExceptionType(self):
+ with self.assertRaises(TypeError):
+ self.assertRaisesRegex()
+ with self.assertRaises(TypeError):
+ self.assertRaisesRegex(ValueError)
+ with self.assertRaises(TypeError):
+ self.assertRaisesRegex(1, 'expect')
+ with self.assertRaises(TypeError):
+ self.assertRaisesRegex(object, 'expect')
+ with self.assertRaises(TypeError):
+ self.assertRaisesRegex((ValueError, 1), 'expect')
+ with self.assertRaises(TypeError):
+ self.assertRaisesRegex((ValueError, object), 'expect')
+
def testAssertWarnsCallable(self):
def _runtime_warn():
warnings.warn("foo", RuntimeWarning)
@@ -1251,6 +1298,9 @@ test case
# Failure when no warning is triggered
with self.assertRaises(self.failureException):
self.assertWarns(RuntimeWarning, lambda: 0)
+ # Failure when the function is None
+ with self.assertWarns(DeprecationWarning):
+ self.assertWarns(RuntimeWarning, None)
# Failure when another warning is triggered
with warnings.catch_warnings():
# Force default filter (in case tests are run with -We)
@@ -1289,6 +1339,15 @@ test case
with self.assertRaises(self.failureException):
with self.assertWarns(RuntimeWarning):
pass
+ # Custom message
+ with self.assertRaisesRegex(self.failureException, 'foobar'):
+ with self.assertWarns(RuntimeWarning, msg='foobar'):
+ pass
+ # Invalid keyword argument
+ with self.assertWarnsRegex(DeprecationWarning, 'foobar'), \
+ self.assertRaises(AssertionError):
+ with self.assertWarns(RuntimeWarning, foobar=42):
+ pass
# Failure when another warning is triggered
with warnings.catch_warnings():
# Force default filter (in case tests are run with -We)
@@ -1303,6 +1362,20 @@ test case
with self.assertWarns(DeprecationWarning):
_runtime_warn()
+ def testAssertWarnsNoExceptionType(self):
+ with self.assertRaises(TypeError):
+ self.assertWarns()
+ with self.assertRaises(TypeError):
+ self.assertWarns(1)
+ with self.assertRaises(TypeError):
+ self.assertWarns(object)
+ with self.assertRaises(TypeError):
+ self.assertWarns((UserWarning, 1))
+ with self.assertRaises(TypeError):
+ self.assertWarns((UserWarning, object))
+ with self.assertRaises(TypeError):
+ self.assertWarns((UserWarning, Exception))
+
def testAssertWarnsRegexCallable(self):
def _runtime_warn(msg):
warnings.warn(msg, RuntimeWarning)
@@ -1312,6 +1385,9 @@ test case
with self.assertRaises(self.failureException):
self.assertWarnsRegex(RuntimeWarning, "o+",
lambda: 0)
+ # Failure when the function is None
+ with self.assertWarns(DeprecationWarning):
+ self.assertWarnsRegex(RuntimeWarning, "o+", None)
# Failure when another warning is triggered
with warnings.catch_warnings():
# Force default filter (in case tests are run with -We)
@@ -1348,6 +1424,15 @@ test case
with self.assertRaises(self.failureException):
with self.assertWarnsRegex(RuntimeWarning, "o+"):
pass
+ # Custom message
+ with self.assertRaisesRegex(self.failureException, 'foobar'):
+ with self.assertWarnsRegex(RuntimeWarning, 'o+', msg='foobar'):
+ pass
+ # Invalid keyword argument
+ with self.assertWarnsRegex(DeprecationWarning, 'foobar'), \
+ self.assertRaises(AssertionError):
+ with self.assertWarnsRegex(RuntimeWarning, 'o+', foobar=42):
+ pass
# Failure when another warning is triggered
with warnings.catch_warnings():
# Force default filter (in case tests are run with -We)
@@ -1369,6 +1454,22 @@ test case
with self.assertWarnsRegex(RuntimeWarning, "o+"):
_runtime_warn("barz")
+ def testAssertWarnsRegexNoExceptionType(self):
+ with self.assertRaises(TypeError):
+ self.assertWarnsRegex()
+ with self.assertRaises(TypeError):
+ self.assertWarnsRegex(UserWarning)
+ with self.assertRaises(TypeError):
+ self.assertWarnsRegex(1, 'expect')
+ with self.assertRaises(TypeError):
+ self.assertWarnsRegex(object, 'expect')
+ with self.assertRaises(TypeError):
+ self.assertWarnsRegex((UserWarning, 1), 'expect')
+ with self.assertRaises(TypeError):
+ self.assertWarnsRegex((UserWarning, object), 'expect')
+ with self.assertRaises(TypeError):
+ self.assertWarnsRegex((UserWarning, Exception), 'expect')
+
@contextlib.contextmanager
def assertNoStderr(self):
with captured_stderr() as buf:
diff --git a/Lib/unittest/test/test_discovery.py b/Lib/unittest/test/test_discovery.py
index f12e8983cd..55921febf5 100644
--- a/Lib/unittest/test/test_discovery.py
+++ b/Lib/unittest/test/test_discovery.py
@@ -1,4 +1,5 @@
-import os
+import os.path
+from os.path import abspath
import re
import sys
import types
@@ -69,7 +70,13 @@ class TestDiscovery(unittest.TestCase):
self.addCleanup(restore_isfile)
loader._get_module_from_name = lambda path: path + ' module'
- loader.loadTestsFromModule = lambda module: module + ' tests'
+ orig_load_tests = loader.loadTestsFromModule
+ def loadTestsFromModule(module, pattern=None):
+ # This is where load_tests is called.
+ base = orig_load_tests(module, pattern=pattern)
+ return base + [module + ' tests']
+ loader.loadTestsFromModule = loadTestsFromModule
+ loader.suiteClass = lambda thing: thing
top_level = os.path.abspath('/foo')
loader._top_level_dir = top_level
@@ -77,9 +84,9 @@ class TestDiscovery(unittest.TestCase):
# 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
+ expected = [[name + ' module tests'] for name in
+ ('test1', 'test2', 'test_dir')]
+ expected.extend([[('test_dir.%s' % name) + ' module tests'] for name in
('test3', 'test4')])
self.assertEqual(suite, expected)
@@ -117,34 +124,204 @@ class TestDiscovery(unittest.TestCase):
if os.path.basename(path) == 'test_directory':
def load_tests(loader, tests, pattern):
self.load_tests_args.append((loader, tests, pattern))
- return 'load_tests'
+ return [self.path + ' load_tests']
self.load_tests = load_tests
def __eq__(self, other):
return self.path == other.path
loader._get_module_from_name = lambda name: Module(name)
- def loadTestsFromModule(module, use_load_tests):
- if use_load_tests:
- raise self.failureException('use_load_tests should be False for packages')
- return module.path + ' module tests'
+ orig_load_tests = loader.loadTestsFromModule
+ def loadTestsFromModule(module, pattern=None):
+ # This is where load_tests is called.
+ base = orig_load_tests(module, pattern=pattern)
+ return base + [module.path + ' module tests']
loader.loadTestsFromModule = loadTestsFromModule
+ loader.suiteClass = lambda thing: thing
loader._top_level_dir = '/foo'
# this time no '.py' on the pattern so that it can match
# a test package
suite = list(loader._find_tests('/foo', 'test*'))
- # We should have loaded tests from the test_directory package by calling load_tests
- # and directly from the test_directory2 package
+ # We should have loaded tests from the a_directory and test_directory2
+ # directly and via load_tests for the test_directory package, which
+ # still calls the baseline module loader.
self.assertEqual(suite,
- ['load_tests', 'test_directory2' + ' module tests'])
+ [['a_directory module tests'],
+ ['test_directory load_tests',
+ 'test_directory module tests'],
+ ['test_directory2 module tests']])
+
+
# The test module paths should be sorted for reliable execution order
- self.assertEqual(Module.paths, ['test_directory', 'test_directory2'])
+ self.assertEqual(Module.paths,
+ ['a_directory', 'test_directory', 'test_directory2'])
# load_tests should have been called once with loader, tests and pattern
+ # (but there are no tests in our stub module itself, so thats [] at the
+ # time of call.
+ self.assertEqual(Module.load_tests_args,
+ [(loader, [], 'test*')])
+
+ def test_find_tests_default_calls_package_load_tests(self):
+ loader = unittest.TestLoader()
+
+ original_listdir = os.listdir
+ def restore_listdir():
+ os.listdir = original_listdir
+ original_isfile = os.path.isfile
+ def restore_isfile():
+ os.path.isfile = original_isfile
+ original_isdir = os.path.isdir
+ def restore_isdir():
+ os.path.isdir = original_isdir
+
+ directories = ['a_directory', 'test_directory', 'test_directory2']
+ path_lists = [directories, [], [], []]
+ os.listdir = lambda path: path_lists.pop(0)
+ self.addCleanup(restore_listdir)
+
+ os.path.isdir = lambda path: True
+ self.addCleanup(restore_isdir)
+
+ os.path.isfile = lambda path: os.path.basename(path) not in directories
+ self.addCleanup(restore_isfile)
+
+ class Module(object):
+ paths = []
+ load_tests_args = []
+
+ def __init__(self, path):
+ self.path = path
+ self.paths.append(path)
+ if os.path.basename(path) == 'test_directory':
+ def load_tests(loader, tests, pattern):
+ self.load_tests_args.append((loader, tests, pattern))
+ return [self.path + ' load_tests']
+ self.load_tests = load_tests
+
+ def __eq__(self, other):
+ return self.path == other.path
+
+ loader._get_module_from_name = lambda name: Module(name)
+ orig_load_tests = loader.loadTestsFromModule
+ def loadTestsFromModule(module, pattern=None):
+ # This is where load_tests is called.
+ base = orig_load_tests(module, pattern=pattern)
+ return base + [module.path + ' module tests']
+ loader.loadTestsFromModule = loadTestsFromModule
+ loader.suiteClass = lambda thing: thing
+
+ loader._top_level_dir = '/foo'
+ # this time no '.py' on the pattern so that it can match
+ # a test package
+ suite = list(loader._find_tests('/foo', 'test*.py'))
+
+ # We should have loaded tests from the a_directory and test_directory2
+ # directly and via load_tests for the test_directory package, which
+ # still calls the baseline module loader.
+ self.assertEqual(suite,
+ [['a_directory module tests'],
+ ['test_directory load_tests',
+ 'test_directory module tests'],
+ ['test_directory2 module tests']])
+ # The test module paths should be sorted for reliable execution order
+ self.assertEqual(Module.paths,
+ ['a_directory', 'test_directory', 'test_directory2'])
+
+
+ # load_tests should have been called once with loader, tests and pattern
+ self.assertEqual(Module.load_tests_args,
+ [(loader, [], 'test*.py')])
+
+ def test_find_tests_customise_via_package_pattern(self):
+ # This test uses the example 'do-nothing' load_tests from
+ # https://docs.python.org/3/library/unittest.html#load-tests-protocol
+ # to make sure that that actually works.
+ # Housekeeping
+ original_listdir = os.listdir
+ def restore_listdir():
+ os.listdir = original_listdir
+ self.addCleanup(restore_listdir)
+ original_isfile = os.path.isfile
+ def restore_isfile():
+ os.path.isfile = original_isfile
+ self.addCleanup(restore_isfile)
+ original_isdir = os.path.isdir
+ def restore_isdir():
+ os.path.isdir = original_isdir
+ self.addCleanup(restore_isdir)
+ self.addCleanup(sys.path.remove, abspath('/foo'))
+
+ # Test data: we expect the following:
+ # a listdir to find our package, and isfile and isdir checks on it.
+ # a module-from-name call to turn that into a module
+ # followed by load_tests.
+ # then our load_tests will call discover() which is messy
+ # but that finally chains into find_tests again for the child dir -
+ # which is why we don't have an infinite loop.
+ # We expect to see:
+ # the module load tests for both package and plain module called,
+ # and the plain module result nested by the package module load_tests
+ # indicating that it was processed and could have been mutated.
+ vfs = {abspath('/foo'): ['my_package'],
+ abspath('/foo/my_package'): ['__init__.py', 'test_module.py']}
+ def list_dir(path):
+ return list(vfs[path])
+ os.listdir = list_dir
+ os.path.isdir = lambda path: not path.endswith('.py')
+ os.path.isfile = lambda path: path.endswith('.py')
+
+ class Module(object):
+ paths = []
+ load_tests_args = []
+
+ def __init__(self, path):
+ self.path = path
+ self.paths.append(path)
+ if path.endswith('test_module'):
+ def load_tests(loader, tests, pattern):
+ self.load_tests_args.append((loader, tests, pattern))
+ return [self.path + ' load_tests']
+ else:
+ def load_tests(loader, tests, pattern):
+ self.load_tests_args.append((loader, tests, pattern))
+ # top level directory cached on loader instance
+ __file__ = '/foo/my_package/__init__.py'
+ this_dir = os.path.dirname(__file__)
+ pkg_tests = loader.discover(
+ start_dir=this_dir, pattern=pattern)
+ return [self.path + ' load_tests', tests
+ ] + pkg_tests
+ self.load_tests = load_tests
+
+ def __eq__(self, other):
+ return self.path == other.path
+
+ loader = unittest.TestLoader()
+ loader._get_module_from_name = lambda name: Module(name)
+ loader.suiteClass = lambda thing: thing
+
+ loader._top_level_dir = abspath('/foo')
+ # this time no '.py' on the pattern so that it can match
+ # a test package
+ suite = list(loader._find_tests(abspath('/foo'), 'test*.py'))
+
+ # We should have loaded tests from both my_package and
+ # my_pacakge.test_module, and also run the load_tests hook in both.
+ # (normally this would be nested TestSuites.)
+ self.assertEqual(suite,
+ [['my_package load_tests', [],
+ ['my_package.test_module load_tests']]])
+ # Parents before children.
+ self.assertEqual(Module.paths,
+ ['my_package', 'my_package.test_module'])
+
+ # load_tests should have been called twice with loader, tests and pattern
self.assertEqual(Module.load_tests_args,
- [(loader, 'test_directory' + ' module tests', 'test*')])
+ [(loader, [], 'test*.py'),
+ (loader, [], 'test*.py')])
def test_discover(self):
loader = unittest.TestLoader()
@@ -192,6 +369,51 @@ class TestDiscovery(unittest.TestCase):
self.assertEqual(_find_tests_args, [(start_dir, 'pattern')])
self.assertIn(top_level_dir, sys.path)
+ def test_discover_start_dir_is_package_calls_package_load_tests(self):
+ # This test verifies that the package load_tests in a package is indeed
+ # invoked when the start_dir is a package (and not the top level).
+ # http://bugs.python.org/issue22457
+
+ # Test data: we expect the following:
+ # an isfile to verify the package, then importing and scanning
+ # as per _find_tests' normal behaviour.
+ # We expect to see our load_tests hook called once.
+ vfs = {abspath('/toplevel'): ['startdir'],
+ abspath('/toplevel/startdir'): ['__init__.py']}
+ def list_dir(path):
+ return list(vfs[path])
+ self.addCleanup(setattr, os, 'listdir', os.listdir)
+ os.listdir = list_dir
+ self.addCleanup(setattr, os.path, 'isfile', os.path.isfile)
+ os.path.isfile = lambda path: path.endswith('.py')
+ self.addCleanup(setattr, os.path, 'isdir', os.path.isdir)
+ os.path.isdir = lambda path: not path.endswith('.py')
+ self.addCleanup(sys.path.remove, abspath('/toplevel'))
+
+ class Module(object):
+ paths = []
+ load_tests_args = []
+
+ def __init__(self, path):
+ self.path = path
+
+ def load_tests(self, loader, tests, pattern):
+ return ['load_tests called ' + self.path]
+
+ def __eq__(self, other):
+ return self.path == other.path
+
+ loader = unittest.TestLoader()
+ loader._get_module_from_name = lambda name: Module(name)
+ loader.suiteClass = lambda thing: thing
+
+ suite = loader.discover('/toplevel/startdir', top_level_dir='/toplevel')
+
+ # We should have loaded tests from the package __init__.
+ # (normally this would be nested TestSuites.)
+ self.assertEqual(suite,
+ [['load_tests called startdir']])
+
def setup_import_issue_tests(self, fakefile):
listdir = os.listdir
os.listdir = lambda _: [fakefile]
@@ -204,6 +426,17 @@ class TestDiscovery(unittest.TestCase):
sys.path[:] = orig_sys_path
self.addCleanup(restore)
+ def setup_import_issue_package_tests(self, vfs):
+ self.addCleanup(setattr, os, 'listdir', os.listdir)
+ self.addCleanup(setattr, os.path, 'isfile', os.path.isfile)
+ self.addCleanup(setattr, os.path, 'isdir', os.path.isdir)
+ self.addCleanup(sys.path.__setitem__, slice(None), list(sys.path))
+ def list_dir(path):
+ return list(vfs[path])
+ os.listdir = list_dir
+ os.path.isdir = lambda path: not path.endswith('.py')
+ os.path.isfile = lambda path: path.endswith('.py')
+
def test_discover_with_modules_that_fail_to_import(self):
loader = unittest.TestLoader()
@@ -212,11 +445,44 @@ class TestDiscovery(unittest.TestCase):
suite = loader.discover('.')
self.assertIn(os.getcwd(), sys.path)
self.assertEqual(suite.countTestCases(), 1)
+ # Errors loading the suite are also captured for introspection.
+ self.assertNotEqual([], loader.errors)
+ self.assertEqual(1, len(loader.errors))
+ error = loader.errors[0]
+ self.assertTrue(
+ 'Failed to import test module: test_this_does_not_exist' in error,
+ 'missing error string in %r' % error)
test = list(list(suite)[0])[0] # extract test from suite
with self.assertRaises(ImportError):
test.test_this_does_not_exist()
+ def test_discover_with_init_modules_that_fail_to_import(self):
+ vfs = {abspath('/foo'): ['my_package'],
+ abspath('/foo/my_package'): ['__init__.py', 'test_module.py']}
+ self.setup_import_issue_package_tests(vfs)
+ import_calls = []
+ def _get_module_from_name(name):
+ import_calls.append(name)
+ raise ImportError("Cannot import Name")
+ loader = unittest.TestLoader()
+ loader._get_module_from_name = _get_module_from_name
+ suite = loader.discover(abspath('/foo'))
+
+ self.assertIn(abspath('/foo'), sys.path)
+ self.assertEqual(suite.countTestCases(), 1)
+ # Errors loading the suite are also captured for introspection.
+ self.assertNotEqual([], loader.errors)
+ self.assertEqual(1, len(loader.errors))
+ error = loader.errors[0]
+ self.assertTrue(
+ 'Failed to import test module: my_package' in error,
+ 'missing error string in %r' % error)
+ test = list(list(suite)[0])[0] # extract test from suite
+ with self.assertRaises(ImportError):
+ test.my_package()
+ self.assertEqual(import_calls, ['my_package'])
+
# Check picklability
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
pickle.loads(pickle.dumps(test, proto))
@@ -241,6 +507,30 @@ class TestDiscovery(unittest.TestCase):
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
pickle.loads(pickle.dumps(suite, proto))
+ def test_discover_with_init_module_that_raises_SkipTest_on_import(self):
+ vfs = {abspath('/foo'): ['my_package'],
+ abspath('/foo/my_package'): ['__init__.py', 'test_module.py']}
+ self.setup_import_issue_package_tests(vfs)
+ import_calls = []
+ def _get_module_from_name(name):
+ import_calls.append(name)
+ raise unittest.SkipTest('skipperoo')
+ loader = unittest.TestLoader()
+ loader._get_module_from_name = _get_module_from_name
+ suite = loader.discover(abspath('/foo'))
+
+ self.assertIn(abspath('/foo'), sys.path)
+ self.assertEqual(suite.countTestCases(), 1)
+ result = unittest.TestResult()
+ suite.run(result)
+ self.assertEqual(len(result.skipped), 1)
+ self.assertEqual(result.testsRun, 1)
+ self.assertEqual(import_calls, ['my_package'])
+
+ # Check picklability
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ pickle.loads(pickle.dumps(suite, proto))
+
def test_command_line_handling_parseArgs(self):
program = TestableTestProgram()
diff --git a/Lib/unittest/test/test_loader.py b/Lib/unittest/test/test_loader.py
index b62a1b5c54..68f1036111 100644
--- a/Lib/unittest/test/test_loader.py
+++ b/Lib/unittest/test/test_loader.py
@@ -1,12 +1,36 @@
import sys
import types
-
+import warnings
import unittest
+# Decorator used in the deprecation tests to reset the warning registry for
+# test isolation and reproducibility.
+def warningregistry(func):
+ def wrapper(*args, **kws):
+ missing = object()
+ saved = getattr(warnings, '__warningregistry__', missing).copy()
+ try:
+ return func(*args, **kws)
+ finally:
+ if saved is missing:
+ try:
+ del warnings.__warningregistry__
+ except AttributeError:
+ pass
+ else:
+ warnings.__warningregistry__ = saved
+
class Test_TestLoader(unittest.TestCase):
+ ### Basic object tests
+ ################################################################
+
+ def test___init__(self):
+ loader = unittest.TestLoader()
+ self.assertEqual([], loader.errors)
+
### Tests for TestLoader.loadTestsFromTestCase
################################################################
@@ -150,6 +174,7 @@ class Test_TestLoader(unittest.TestCase):
# Check that loadTestsFromModule honors (or not) a module
# with a load_tests function.
+ @warningregistry
def test_loadTestsFromModule__load_tests(self):
m = types.ModuleType('m')
class MyTestCase(unittest.TestCase):
@@ -168,10 +193,144 @@ class Test_TestLoader(unittest.TestCase):
suite = loader.loadTestsFromModule(m)
self.assertIsInstance(suite, unittest.TestSuite)
self.assertEqual(load_tests_args, [loader, suite, None])
+ # With Python 3.5, the undocumented and unofficial use_load_tests is
+ # ignored (and deprecated).
+ load_tests_args = []
+ with warnings.catch_warnings(record=False):
+ warnings.simplefilter('never')
+ suite = loader.loadTestsFromModule(m, use_load_tests=False)
+ self.assertEqual(load_tests_args, [loader, suite, None])
+
+ @warningregistry
+ def test_loadTestsFromModule__use_load_tests_deprecated_positional(self):
+ m = types.ModuleType('m')
+ class MyTestCase(unittest.TestCase):
+ def test(self):
+ pass
+ m.testcase_1 = MyTestCase
+
+ load_tests_args = []
+ def load_tests(loader, tests, pattern):
+ self.assertIsInstance(tests, unittest.TestSuite)
+ load_tests_args.extend((loader, tests, pattern))
+ return tests
+ m.load_tests = load_tests
+ # The method still works.
+ loader = unittest.TestLoader()
+ # use_load_tests=True as a positional argument.
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter('always')
+ suite = loader.loadTestsFromModule(m, False)
+ self.assertIsInstance(suite, unittest.TestSuite)
+ # load_tests was still called because use_load_tests is deprecated
+ # and ignored.
+ self.assertEqual(load_tests_args, [loader, suite, None])
+ # We got a warning.
+ self.assertIs(w[-1].category, DeprecationWarning)
+ self.assertEqual(str(w[-1].message),
+ 'use_load_tests is deprecated and ignored')
+
+ @warningregistry
+ def test_loadTestsFromModule__use_load_tests_deprecated_keyword(self):
+ m = types.ModuleType('m')
+ class MyTestCase(unittest.TestCase):
+ def test(self):
+ pass
+ m.testcase_1 = MyTestCase
+
+ load_tests_args = []
+ def load_tests(loader, tests, pattern):
+ self.assertIsInstance(tests, unittest.TestSuite)
+ load_tests_args.extend((loader, tests, pattern))
+ return tests
+ m.load_tests = load_tests
+ # The method still works.
+ loader = unittest.TestLoader()
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter('always')
+ suite = loader.loadTestsFromModule(m, use_load_tests=False)
+ self.assertIsInstance(suite, unittest.TestSuite)
+ # load_tests was still called because use_load_tests is deprecated
+ # and ignored.
+ self.assertEqual(load_tests_args, [loader, suite, None])
+ # We got a warning.
+ self.assertIs(w[-1].category, DeprecationWarning)
+ self.assertEqual(str(w[-1].message),
+ 'use_load_tests is deprecated and ignored')
+
+ @warningregistry
+ def test_loadTestsFromModule__too_many_positional_args(self):
+ m = types.ModuleType('m')
+ class MyTestCase(unittest.TestCase):
+ def test(self):
+ pass
+ m.testcase_1 = MyTestCase
load_tests_args = []
- suite = loader.loadTestsFromModule(m, use_load_tests=False)
- self.assertEqual(load_tests_args, [])
+ def load_tests(loader, tests, pattern):
+ self.assertIsInstance(tests, unittest.TestSuite)
+ load_tests_args.extend((loader, tests, pattern))
+ return tests
+ m.load_tests = load_tests
+ loader = unittest.TestLoader()
+ with self.assertRaises(TypeError) as cm, \
+ warnings.catch_warning(record=True) as w:
+ loader.loadTestsFromModule(m, False, 'testme.*')
+ # We still got the deprecation warning.
+ self.assertIs(w[-1].category, DeprecationWarning)
+ self.assertEqual(str(w[-1].message),
+ 'use_load_tests is deprecated and ignored')
+ # We also got a TypeError for too many positional arguments.
+ self.assertEqual(type(cm.exception), TypeError)
+ self.assertEqual(
+ str(cm.exception),
+ 'loadTestsFromModule() takes 1 positional argument but 3 were given')
+
+ @warningregistry
+ def test_loadTestsFromModule__use_load_tests_other_bad_keyword(self):
+ m = types.ModuleType('m')
+ class MyTestCase(unittest.TestCase):
+ def test(self):
+ pass
+ m.testcase_1 = MyTestCase
+
+ load_tests_args = []
+ def load_tests(loader, tests, pattern):
+ self.assertIsInstance(tests, unittest.TestSuite)
+ load_tests_args.extend((loader, tests, pattern))
+ return tests
+ m.load_tests = load_tests
+ loader = unittest.TestLoader()
+ with warnings.catch_warnings():
+ warnings.simplefilter('never')
+ with self.assertRaises(TypeError) as cm:
+ loader.loadTestsFromModule(
+ m, use_load_tests=False, very_bad=True, worse=False)
+ self.assertEqual(type(cm.exception), TypeError)
+ # The error message names the first bad argument alphabetically,
+ # however use_load_tests (which sorts first) is ignored.
+ self.assertEqual(
+ str(cm.exception),
+ "loadTestsFromModule() got an unexpected keyword argument 'very_bad'")
+
+ def test_loadTestsFromModule__pattern(self):
+ m = types.ModuleType('m')
+ class MyTestCase(unittest.TestCase):
+ def test(self):
+ pass
+ m.testcase_1 = MyTestCase
+
+ load_tests_args = []
+ def load_tests(loader, tests, pattern):
+ self.assertIsInstance(tests, unittest.TestSuite)
+ load_tests_args.extend((loader, tests, pattern))
+ return tests
+ m.load_tests = load_tests
+
+ loader = unittest.TestLoader()
+ suite = loader.loadTestsFromModule(m, pattern='testme.*')
+ self.assertIsInstance(suite, unittest.TestSuite)
+ self.assertEqual(load_tests_args, [loader, suite, 'testme.*'])
def test_loadTestsFromModule__faulty_load_tests(self):
m = types.ModuleType('m')
@@ -184,6 +343,13 @@ class Test_TestLoader(unittest.TestCase):
suite = loader.loadTestsFromModule(m)
self.assertIsInstance(suite, unittest.TestSuite)
self.assertEqual(suite.countTestCases(), 1)
+ # Errors loading the suite are also captured for introspection.
+ self.assertNotEqual([], loader.errors)
+ self.assertEqual(1, len(loader.errors))
+ error = loader.errors[0]
+ self.assertTrue(
+ 'Failed to call load_tests:' in error,
+ 'missing error string in %r' % error)
test = list(suite)[0]
self.assertRaisesRegex(TypeError, "some failure", test.m)
@@ -219,15 +385,15 @@ class Test_TestLoader(unittest.TestCase):
def test_loadTestsFromName__malformed_name(self):
loader = unittest.TestLoader()
- # XXX Should this raise ValueError or ImportError?
- try:
- loader.loadTestsFromName('abc () //')
- except ValueError:
- pass
- except ImportError:
- pass
- else:
- self.fail("TestLoader.loadTestsFromName failed to raise ValueError")
+ suite = loader.loadTestsFromName('abc () //')
+ error, test = self.check_deferred_error(loader, suite)
+ expected = "Failed to import test module: abc () //"
+ expected_regex = "Failed to import test module: abc \(\) //"
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(
+ ImportError, expected_regex, getattr(test, 'abc () //'))
# "The specifier name is a ``dotted name'' that may resolve ... to a
# module"
@@ -236,28 +402,47 @@ class Test_TestLoader(unittest.TestCase):
def test_loadTestsFromName__unknown_module_name(self):
loader = unittest.TestLoader()
- try:
- loader.loadTestsFromName('sdasfasfasdf')
- except ImportError as e:
- self.assertEqual(str(e), "No module named 'sdasfasfasdf'")
- else:
- self.fail("TestLoader.loadTestsFromName failed to raise ImportError")
+ suite = loader.loadTestsFromName('sdasfasfasdf')
+ expected = "No module named 'sdasfasfasdf'"
+ error, test = self.check_deferred_error(loader, suite)
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(ImportError, expected, test.sdasfasfasdf)
# "The specifier name is a ``dotted name'' that may resolve either to
# a module, a test case class, a TestSuite instance, a test method
# within a test case class, or a callable object which returns a
# TestCase or TestSuite instance."
#
- # What happens when the module is found, but the attribute can't?
- def test_loadTestsFromName__unknown_attr_name(self):
+ # What happens when the module is found, but the attribute isn't?
+ def test_loadTestsFromName__unknown_attr_name_on_module(self):
loader = unittest.TestLoader()
- try:
- loader.loadTestsFromName('unittest.sdasfasfasdf')
- except AttributeError as e:
- self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'")
- else:
- self.fail("TestLoader.loadTestsFromName failed to raise AttributeError")
+ suite = loader.loadTestsFromName('unittest.loader.sdasfasfasdf')
+ expected = "module 'unittest.loader' has no attribute 'sdasfasfasdf'"
+ error, test = self.check_deferred_error(loader, suite)
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(AttributeError, expected, test.sdasfasfasdf)
+
+ # "The specifier name is a ``dotted name'' that may resolve either to
+ # a module, a test case class, a TestSuite instance, a test method
+ # within a test case class, or a callable object which returns a
+ # TestCase or TestSuite instance."
+ #
+ # What happens when the module is found, but the attribute isn't?
+ def test_loadTestsFromName__unknown_attr_name_on_package(self):
+ loader = unittest.TestLoader()
+
+ suite = loader.loadTestsFromName('unittest.sdasfasfasdf')
+ expected = "No module named 'unittest.sdasfasfasdf'"
+ error, test = self.check_deferred_error(loader, suite)
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(ImportError, expected, test.sdasfasfasdf)
# "The specifier name is a ``dotted name'' that may resolve either to
# a module, a test case class, a TestSuite instance, a test method
@@ -269,12 +454,13 @@ class Test_TestLoader(unittest.TestCase):
def test_loadTestsFromName__relative_unknown_name(self):
loader = unittest.TestLoader()
- try:
- loader.loadTestsFromName('sdasfasfasdf', unittest)
- except AttributeError as e:
- self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'")
- else:
- self.fail("TestLoader.loadTestsFromName failed to raise AttributeError")
+ suite = loader.loadTestsFromName('sdasfasfasdf', unittest)
+ expected = "module 'unittest' has no attribute 'sdasfasfasdf'"
+ error, test = self.check_deferred_error(loader, suite)
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(AttributeError, expected, test.sdasfasfasdf)
# "The specifier name is a ``dotted name'' that may resolve either to
# a module, a test case class, a TestSuite instance, a test method
@@ -290,12 +476,13 @@ class Test_TestLoader(unittest.TestCase):
def test_loadTestsFromName__relative_empty_name(self):
loader = unittest.TestLoader()
- try:
- loader.loadTestsFromName('', unittest)
- except AttributeError as e:
- pass
- else:
- self.fail("Failed to raise AttributeError")
+ suite = loader.loadTestsFromName('', unittest)
+ error, test = self.check_deferred_error(loader, suite)
+ expected = "has no attribute ''"
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(AttributeError, expected, getattr(test, ''))
# "The specifier name is a ``dotted name'' that may resolve either to
# a module, a test case class, a TestSuite instance, a test method
@@ -310,14 +497,15 @@ class Test_TestLoader(unittest.TestCase):
loader = unittest.TestLoader()
# XXX Should this raise AttributeError or ValueError?
- try:
- loader.loadTestsFromName('abc () //', unittest)
- except ValueError:
- pass
- except AttributeError:
- pass
- else:
- self.fail("TestLoader.loadTestsFromName failed to raise ValueError")
+ suite = loader.loadTestsFromName('abc () //', unittest)
+ error, test = self.check_deferred_error(loader, suite)
+ expected = "module 'unittest' has no attribute 'abc () //'"
+ expected_regex = "module 'unittest' has no attribute 'abc \(\) //'"
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(
+ AttributeError, expected_regex, getattr(test, 'abc () //'))
# "The method optionally resolves name relative to the given module"
#
@@ -423,12 +611,13 @@ class Test_TestLoader(unittest.TestCase):
m.testcase_1 = MyTestCase
loader = unittest.TestLoader()
- try:
- loader.loadTestsFromName('testcase_1.testfoo', m)
- except AttributeError as e:
- self.assertEqual(str(e), "type object 'MyTestCase' has no attribute 'testfoo'")
- else:
- self.fail("Failed to raise AttributeError")
+ suite = loader.loadTestsFromName('testcase_1.testfoo', m)
+ expected = "type object 'MyTestCase' has no attribute 'testfoo'"
+ error, test = self.check_deferred_error(loader, suite)
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(AttributeError, expected, test.testfoo)
# "The specifier name is a ``dotted name'' that may resolve ... to
# ... a callable object which returns a ... TestSuite instance"
@@ -546,6 +735,23 @@ class Test_TestLoader(unittest.TestCase):
### Tests for TestLoader.loadTestsFromNames()
################################################################
+ def check_deferred_error(self, loader, suite):
+ """Helper function for checking that errors in loading are reported.
+
+ :param loader: A loader with some errors.
+ :param suite: A suite that should have a late bound error.
+ :return: The first error message from the loader and the test object
+ from the suite.
+ """
+ self.assertIsInstance(suite, unittest.TestSuite)
+ self.assertEqual(suite.countTestCases(), 1)
+ # Errors loading the suite are also captured for introspection.
+ self.assertNotEqual([], loader.errors)
+ self.assertEqual(1, len(loader.errors))
+ error = loader.errors[0]
+ test = list(suite)[0]
+ return error, test
+
# "Similar to loadTestsFromName(), but takes a sequence of names rather
# than a single name."
#
@@ -598,14 +804,15 @@ class Test_TestLoader(unittest.TestCase):
loader = unittest.TestLoader()
# XXX Should this raise ValueError or ImportError?
- try:
- loader.loadTestsFromNames(['abc () //'])
- except ValueError:
- pass
- except ImportError:
- pass
- else:
- self.fail("TestLoader.loadTestsFromNames failed to raise ValueError")
+ suite = loader.loadTestsFromNames(['abc () //'])
+ error, test = self.check_deferred_error(loader, list(suite)[0])
+ expected = "Failed to import test module: abc () //"
+ expected_regex = "Failed to import test module: abc \(\) //"
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(
+ ImportError, expected_regex, getattr(test, 'abc () //'))
# "The specifier name is a ``dotted name'' that may resolve either to
# a module, a test case class, a TestSuite instance, a test method
@@ -616,12 +823,13 @@ class Test_TestLoader(unittest.TestCase):
def test_loadTestsFromNames__unknown_module_name(self):
loader = unittest.TestLoader()
- try:
- loader.loadTestsFromNames(['sdasfasfasdf'])
- except ImportError as e:
- self.assertEqual(str(e), "No module named 'sdasfasfasdf'")
- else:
- self.fail("TestLoader.loadTestsFromNames failed to raise ImportError")
+ suite = loader.loadTestsFromNames(['sdasfasfasdf'])
+ error, test = self.check_deferred_error(loader, list(suite)[0])
+ expected = "Failed to import test module: sdasfasfasdf"
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(ImportError, expected, test.sdasfasfasdf)
# "The specifier name is a ``dotted name'' that may resolve either to
# a module, a test case class, a TestSuite instance, a test method
@@ -632,12 +840,14 @@ class Test_TestLoader(unittest.TestCase):
def test_loadTestsFromNames__unknown_attr_name(self):
loader = unittest.TestLoader()
- try:
- loader.loadTestsFromNames(['unittest.sdasfasfasdf', 'unittest'])
- except AttributeError as e:
- self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'")
- else:
- self.fail("TestLoader.loadTestsFromNames failed to raise AttributeError")
+ suite = loader.loadTestsFromNames(
+ ['unittest.loader.sdasfasfasdf', 'unittest.test.dummy'])
+ error, test = self.check_deferred_error(loader, list(suite)[0])
+ expected = "module 'unittest.loader' has no attribute 'sdasfasfasdf'"
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(AttributeError, expected, test.sdasfasfasdf)
# "The specifier name is a ``dotted name'' that may resolve either to
# a module, a test case class, a TestSuite instance, a test method
@@ -651,12 +861,13 @@ class Test_TestLoader(unittest.TestCase):
def test_loadTestsFromNames__unknown_name_relative_1(self):
loader = unittest.TestLoader()
- try:
- loader.loadTestsFromNames(['sdasfasfasdf'], unittest)
- except AttributeError as e:
- self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'")
- else:
- self.fail("TestLoader.loadTestsFromName failed to raise AttributeError")
+ suite = loader.loadTestsFromNames(['sdasfasfasdf'], unittest)
+ error, test = self.check_deferred_error(loader, list(suite)[0])
+ expected = "module 'unittest' has no attribute 'sdasfasfasdf'"
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(AttributeError, expected, test.sdasfasfasdf)
# "The specifier name is a ``dotted name'' that may resolve either to
# a module, a test case class, a TestSuite instance, a test method
@@ -670,12 +881,13 @@ class Test_TestLoader(unittest.TestCase):
def test_loadTestsFromNames__unknown_name_relative_2(self):
loader = unittest.TestLoader()
- try:
- loader.loadTestsFromNames(['TestCase', 'sdasfasfasdf'], unittest)
- except AttributeError as e:
- self.assertEqual(str(e), "'module' object has no attribute 'sdasfasfasdf'")
- else:
- self.fail("TestLoader.loadTestsFromName failed to raise AttributeError")
+ suite = loader.loadTestsFromNames(['TestCase', 'sdasfasfasdf'], unittest)
+ error, test = self.check_deferred_error(loader, list(suite)[1])
+ expected = "module 'unittest' has no attribute 'sdasfasfasdf'"
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(AttributeError, expected, test.sdasfasfasdf)
# "The specifier name is a ``dotted name'' that may resolve either to
# a module, a test case class, a TestSuite instance, a test method
@@ -691,12 +903,13 @@ class Test_TestLoader(unittest.TestCase):
def test_loadTestsFromNames__relative_empty_name(self):
loader = unittest.TestLoader()
- try:
- loader.loadTestsFromNames([''], unittest)
- except AttributeError:
- pass
- else:
- self.fail("Failed to raise ValueError")
+ suite = loader.loadTestsFromNames([''], unittest)
+ error, test = self.check_deferred_error(loader, list(suite)[0])
+ expected = "has no attribute ''"
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(AttributeError, expected, getattr(test, ''))
# "The specifier name is a ``dotted name'' that may resolve either to
# a module, a test case class, a TestSuite instance, a test method
@@ -710,14 +923,15 @@ class Test_TestLoader(unittest.TestCase):
loader = unittest.TestLoader()
# XXX Should this raise AttributeError or ValueError?
- try:
- loader.loadTestsFromNames(['abc () //'], unittest)
- except AttributeError:
- pass
- except ValueError:
- pass
- else:
- self.fail("TestLoader.loadTestsFromNames failed to raise ValueError")
+ suite = loader.loadTestsFromNames(['abc () //'], unittest)
+ error, test = self.check_deferred_error(loader, list(suite)[0])
+ expected = "module 'unittest' has no attribute 'abc () //'"
+ expected_regex = "module 'unittest' has no attribute 'abc \(\) //'"
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(
+ AttributeError, expected_regex, getattr(test, 'abc () //'))
# "The method optionally resolves name relative to the given module"
#
@@ -835,12 +1049,13 @@ class Test_TestLoader(unittest.TestCase):
m.testcase_1 = MyTestCase
loader = unittest.TestLoader()
- try:
- loader.loadTestsFromNames(['testcase_1.testfoo'], m)
- except AttributeError as e:
- self.assertEqual(str(e), "type object 'MyTestCase' has no attribute 'testfoo'")
- else:
- self.fail("Failed to raise AttributeError")
+ suite = loader.loadTestsFromNames(['testcase_1.testfoo'], m)
+ error, test = self.check_deferred_error(loader, list(suite)[0])
+ expected = "type object 'MyTestCase' has no attribute 'testfoo'"
+ self.assertIn(
+ expected, error,
+ 'missing error string in %r' % error)
+ self.assertRaisesRegex(AttributeError, expected, test.testfoo)
# "The specifier name is a ``dotted name'' that may resolve ... to
# ... a callable object which returns a ... TestSuite instance"
diff --git a/Lib/unittest/test/test_program.py b/Lib/unittest/test/test_program.py
index 725d67fdaf..1cfc17959e 100644
--- a/Lib/unittest/test/test_program.py
+++ b/Lib/unittest/test/test_program.py
@@ -134,6 +134,7 @@ class InitialisableProgram(unittest.TestProgram):
result = None
verbosity = 1
defaultTest = None
+ tb_locals = False
testRunner = None
testLoader = unittest.defaultTestLoader
module = '__main__'
@@ -147,18 +148,19 @@ RESULT = object()
class FakeRunner(object):
initArgs = None
test = None
- raiseError = False
+ raiseError = 0
def __init__(self, **kwargs):
FakeRunner.initArgs = kwargs
if FakeRunner.raiseError:
- FakeRunner.raiseError = False
+ FakeRunner.raiseError -= 1
raise TypeError
def run(self, test):
FakeRunner.test = test
return RESULT
+
class TestCommandLineArgs(unittest.TestCase):
def setUp(self):
@@ -166,7 +168,7 @@ class TestCommandLineArgs(unittest.TestCase):
self.program.createTests = lambda: None
FakeRunner.initArgs = None
FakeRunner.test = None
- FakeRunner.raiseError = False
+ FakeRunner.raiseError = 0
def testVerbosity(self):
program = self.program
@@ -256,6 +258,7 @@ class TestCommandLineArgs(unittest.TestCase):
self.assertEqual(FakeRunner.initArgs, {'verbosity': 'verbosity',
'failfast': 'failfast',
'buffer': 'buffer',
+ 'tb_locals': False,
'warnings': 'warnings'})
self.assertEqual(FakeRunner.test, 'test')
self.assertIs(program.result, RESULT)
@@ -274,10 +277,25 @@ class TestCommandLineArgs(unittest.TestCase):
self.assertEqual(FakeRunner.test, 'test')
self.assertIs(program.result, RESULT)
+ def test_locals(self):
+ program = self.program
+
+ program.testRunner = FakeRunner
+ program.parseArgs([None, '--locals'])
+ self.assertEqual(True, program.tb_locals)
+ program.runTests()
+ self.assertEqual(FakeRunner.initArgs, {'buffer': False,
+ 'failfast': False,
+ 'tb_locals': True,
+ 'verbosity': 1,
+ 'warnings': None})
+
def testRunTestsOldRunnerClass(self):
program = self.program
- FakeRunner.raiseError = True
+ # Two TypeErrors are needed to fall all the way back to old-style
+ # runners - one to fail tb_locals, one to fail buffer etc.
+ FakeRunner.raiseError = 2
program.testRunner = FakeRunner
program.verbosity = 'verbosity'
program.failfast = 'failfast'
diff --git a/Lib/unittest/test/test_result.py b/Lib/unittest/test/test_result.py
index 489fe17754..e39e2eaeca 100644
--- a/Lib/unittest/test/test_result.py
+++ b/Lib/unittest/test/test_result.py
@@ -8,6 +8,20 @@ import traceback
import unittest
+class MockTraceback(object):
+ class TracebackException:
+ def __init__(self, *args, **kwargs):
+ self.capture_locals = kwargs.get('capture_locals', False)
+ def format(self):
+ result = ['A traceback']
+ if self.capture_locals:
+ result.append('locals')
+ return result
+
+def restore_traceback():
+ unittest.result.traceback = traceback
+
+
class Test_TestResult(unittest.TestCase):
# Note: there are not separate tests for TestResult.wasSuccessful(),
# TestResult.errors, TestResult.failures, TestResult.testsRun or
@@ -227,6 +241,25 @@ class Test_TestResult(unittest.TestCase):
self.assertIs(test_case, test)
self.assertIsInstance(formatted_exc, str)
+ def test_addError_locals(self):
+ class Foo(unittest.TestCase):
+ def test_1(self):
+ 1/0
+
+ test = Foo('test_1')
+ result = unittest.TestResult()
+ result.tb_locals = True
+
+ unittest.result.traceback = MockTraceback
+ self.addCleanup(restore_traceback)
+ result.startTestRun()
+ test.run(result)
+ result.stopTestRun()
+
+ self.assertEqual(len(result.errors), 1)
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual('A tracebacklocals', formatted_exc)
+
def test_addSubTest(self):
class Foo(unittest.TestCase):
def test_1(self):
@@ -398,6 +431,7 @@ def __init__(self, stream=None, descriptions=None, verbosity=None):
self.testsRun = 0
self.shouldStop = False
self.buffer = False
+ self.tb_locals = False
classDict['__init__'] = __init__
OldResult = type('OldResult', (object,), classDict)
@@ -454,15 +488,6 @@ class Test_OldTestResult(unittest.TestCase):
runner.run(Test('testFoo'))
-class MockTraceback(object):
- @staticmethod
- def format_exception(*_):
- return ['A traceback']
-
-def restore_traceback():
- unittest.result.traceback = traceback
-
-
class TestOutputBuffering(unittest.TestCase):
def setUp(self):
diff --git a/Lib/unittest/test/test_runner.py b/Lib/unittest/test/test_runner.py
index 7c0bd51d79..9cbc26041f 100644
--- a/Lib/unittest/test/test_runner.py
+++ b/Lib/unittest/test/test_runner.py
@@ -158,7 +158,7 @@ class Test_TextTestRunner(unittest.TestCase):
self.assertEqual(runner.warnings, None)
self.assertTrue(runner.descriptions)
self.assertEqual(runner.resultclass, unittest.TextTestResult)
-
+ self.assertFalse(runner.tb_locals)
def test_multiple_inheritance(self):
class AResult(unittest.TestResult):
@@ -172,14 +172,13 @@ class Test_TextTestRunner(unittest.TestCase):
# 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)
+ buffer=True)
# Use our result object
runner._makeResult = lambda: result
runner.run(Test('testFoo'))
@@ -187,6 +186,11 @@ class Test_TextTestRunner(unittest.TestCase):
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):
diff --git a/Lib/unittest/test/test_setups.py b/Lib/unittest/test/test_setups.py
index 392f95efc0..2df703ed93 100644
--- a/Lib/unittest/test/test_setups.py
+++ b/Lib/unittest/test/test_setups.py
@@ -111,7 +111,7 @@ class TestSetups(unittest.TestCase):
self.assertEqual(len(result.errors), 1)
error, _ = result.errors[0]
self.assertEqual(str(error),
- 'setUpClass (%s.BrokenTest)' % __name__)
+ 'setUpClass (%s.%s)' % (__name__, BrokenTest.__qualname__))
def test_error_in_teardown_class(self):
class Test(unittest.TestCase):
@@ -144,7 +144,7 @@ class TestSetups(unittest.TestCase):
error, _ = result.errors[0]
self.assertEqual(str(error),
- 'tearDownClass (%s.Test)' % __name__)
+ 'tearDownClass (%s.%s)' % (__name__, Test.__qualname__))
def test_class_not_torndown_when_setup_fails(self):
class Test(unittest.TestCase):
@@ -414,7 +414,8 @@ class TestSetups(unittest.TestCase):
self.assertEqual(len(result.errors), 0)
self.assertEqual(len(result.skipped), 1)
skipped = result.skipped[0][0]
- self.assertEqual(str(skipped), 'setUpClass (%s.Test)' % __name__)
+ self.assertEqual(str(skipped),
+ 'setUpClass (%s.%s)' % (__name__, Test.__qualname__))
def test_skiptest_in_setupmodule(self):
class Test(unittest.TestCase):
diff --git a/Lib/unittest/test/testmock/testmagicmethods.py b/Lib/unittest/test/testmock/testmagicmethods.py
index e05c6e014d..bb9b956bb2 100644
--- a/Lib/unittest/test/testmock/testmagicmethods.py
+++ b/Lib/unittest/test/testmock/testmagicmethods.py
@@ -424,6 +424,17 @@ class TestMockingMagicMethods(unittest.TestCase):
self.assertEqual(list(m), [])
+ def test_matmul(self):
+ m = MagicMock()
+ self.assertIsInstance(m @ 1, MagicMock)
+ m.__matmul__.return_value = 42
+ m.__rmatmul__.return_value = 666
+ m.__imatmul__.return_value = 24
+ self.assertEqual(m @ 1, 42)
+ self.assertEqual(1 @ m, 666)
+ m @= 24
+ self.assertEqual(m, 24)
+
def test_divmod_and_rdivmod(self):
m = MagicMock()
self.assertIsInstance(divmod(5, m), MagicMock)
diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py
index cf1673c6ca..2a6069f66a 100644
--- a/Lib/unittest/test/testmock/testmock.py
+++ b/Lib/unittest/test/testmock/testmock.py
@@ -174,6 +174,15 @@ class MockTest(unittest.TestCase):
self.assertEqual([mock(), mock(), mock()], [3, 2, 1],
"callable side effect not used correctly")
+ def test_autospec_side_effect_exception(self):
+ # Test for issue 23661
+ def f():
+ pass
+
+ mock = create_autospec(f)
+ mock.side_effect = ValueError('Bazinga!')
+ self.assertRaisesRegex(ValueError, 'Bazinga!', mock)
+
@unittest.skipUnless('java' in sys.platform,
'This test only applies to Jython')
def test_java_exception_side_effect(self):
@@ -1194,6 +1203,42 @@ class MockTest(unittest.TestCase):
m = mock.create_autospec(object(), name='sweet_func')
self.assertIn('sweet_func', repr(m))
+ #Issue21238
+ def test_mock_unsafe(self):
+ m = Mock()
+ with self.assertRaises(AttributeError):
+ m.assert_foo_call()
+ with self.assertRaises(AttributeError):
+ m.assret_foo_call()
+ m = Mock(unsafe=True)
+ m.assert_foo_call()
+ m.assret_foo_call()
+
+ #Issue21262
+ def test_assert_not_called(self):
+ m = Mock()
+ m.hello.assert_not_called()
+ m.hello()
+ with self.assertRaises(AssertionError):
+ m.hello.assert_not_called()
+
+ #Issue21256 printout of keyword args should be in deterministic order
+ def test_sorted_call_signature(self):
+ m = Mock()
+ m.hello(name='hello', daddy='hero')
+ text = "call(daddy='hero', name='hello')"
+ self.assertEqual(repr(m.hello.call_args), text)
+
+ #Issue21270 overrides tuple methods for mock.call objects
+ def test_override_tuple_methods(self):
+ c = call.count()
+ i = call.index(132,'hello')
+ m = Mock()
+ m.count()
+ m.index(132,"hello")
+ self.assertEqual(m.method_calls[0], c)
+ self.assertEqual(m.method_calls[1], i)
+
def test_mock_add_spec(self):
class _One(object):
one = 1
diff --git a/Lib/unittest/test/testmock/testpatch.py b/Lib/unittest/test/testmock/testpatch.py
index b516f42af0..28fe86b0de 100644
--- a/Lib/unittest/test/testmock/testpatch.py
+++ b/Lib/unittest/test/testmock/testpatch.py
@@ -377,7 +377,7 @@ class PatchTest(unittest.TestCase):
def test_patchobject_wont_create_by_default(self):
try:
- @patch.object(SomeClass, 'frooble', sentinel.Frooble)
+ @patch.object(SomeClass, 'ord', sentinel.Frooble)
def test():
self.fail('Patching non existent attributes should fail')
@@ -386,7 +386,27 @@ class PatchTest(unittest.TestCase):
pass
else:
self.fail('Patching non existent attributes should fail')
- self.assertFalse(hasattr(SomeClass, 'frooble'))
+ self.assertFalse(hasattr(SomeClass, 'ord'))
+
+
+ def test_patch_builtins_without_create(self):
+ @patch(__name__+'.ord')
+ def test_ord(mock_ord):
+ mock_ord.return_value = 101
+ return ord('c')
+
+ @patch(__name__+'.open')
+ def test_open(mock_open):
+ m = mock_open.return_value
+ m.read.return_value = 'abcd'
+
+ fobj = open('doesnotexists.txt')
+ data = fobj.read()
+ fobj.close()
+ return data
+
+ self.assertEqual(test_ord(), 101)
+ self.assertEqual(test_open(), 'abcd')
def test_patch_with_static_methods(self):