summaryrefslogtreecommitdiffstats
path: root/Lib/unittest
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/unittest')
-rw-r--r--Lib/unittest/__init__.py3
-rw-r--r--Lib/unittest/case.py50
-rw-r--r--Lib/unittest/loader.py24
-rw-r--r--Lib/unittest/runner.py9
-rw-r--r--Lib/unittest/test/_test_warnings.py11
-rw-r--r--Lib/unittest/test/test_assertions.py9
-rw-r--r--Lib/unittest/test/test_case.py69
-rw-r--r--Lib/unittest/test/test_loader.py147
-rw-r--r--Lib/unittest/test/test_program.py16
-rw-r--r--Lib/unittest/test/test_runner.py10
10 files changed, 323 insertions, 25 deletions
diff --git a/Lib/unittest/__init__.py b/Lib/unittest/__init__.py
index 540433d..4b18488 100644
--- a/Lib/unittest/__init__.py
+++ b/Lib/unittest/__init__.py
@@ -68,6 +68,9 @@ from .signals import installHandler, registerResult, removeResult, removeHandler
# IsolatedAsyncioTestCase will be imported lazily.
from .loader import makeSuite, getTestCaseNames, findTestCases
+# deprecated
+_TextTestResult = TextTestResult
+
# There are no tests here, so don't try to run anything discovered from
# introspecting the symbols (e.g. FunctionTestCase). Instead, all our
diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py
index f106765..0d55020 100644
--- a/Lib/unittest/case.py
+++ b/Lib/unittest/case.py
@@ -1139,6 +1139,35 @@ class TestCase(object):
standardMsg = self._truncateMessage(standardMsg, diff)
self.fail(self._formatMessage(msg, standardMsg))
+ def assertDictContainsSubset(self, subset, dictionary, msg=None):
+ """Checks whether dictionary is a superset of subset."""
+ warnings.warn('assertDictContainsSubset is deprecated',
+ DeprecationWarning)
+ missing = []
+ mismatched = []
+ for key, value in subset.items():
+ if key not in dictionary:
+ missing.append(key)
+ elif value != dictionary[key]:
+ mismatched.append('%s, expected: %s, actual: %s' %
+ (safe_repr(key), safe_repr(value),
+ safe_repr(dictionary[key])))
+
+ if not (missing or mismatched):
+ return
+
+ standardMsg = ''
+ if missing:
+ standardMsg = 'Missing: %s' % ','.join(safe_repr(m) for m in
+ missing)
+ if mismatched:
+ if standardMsg:
+ standardMsg += '; '
+ standardMsg += 'Mismatched values: %s' % ','.join(mismatched)
+
+ self.fail(self._formatMessage(msg, standardMsg))
+
+
def assertCountEqual(self, first, second, msg=None):
"""Asserts that two iterables have the same elements, the same number of
times, without regard to order.
@@ -1302,6 +1331,27 @@ class TestCase(object):
raise self.failureException(msg)
+ def _deprecate(original_func):
+ def deprecated_func(*args, **kwargs):
+ warnings.warn(
+ 'Please use {0} instead.'.format(original_func.__name__),
+ DeprecationWarning, 2)
+ return original_func(*args, **kwargs)
+ return deprecated_func
+
+ # see #9424
+ failUnlessEqual = assertEquals = _deprecate(assertEqual)
+ failIfEqual = assertNotEquals = _deprecate(assertNotEqual)
+ failUnlessAlmostEqual = assertAlmostEquals = _deprecate(assertAlmostEqual)
+ failIfAlmostEqual = assertNotAlmostEquals = _deprecate(assertNotAlmostEqual)
+ failUnless = assert_ = _deprecate(assertTrue)
+ failUnlessRaises = _deprecate(assertRaises)
+ failIf = _deprecate(assertFalse)
+ assertRaisesRegexp = _deprecate(assertRaisesRegex)
+ assertRegexpMatches = _deprecate(assertRegex)
+ assertNotRegexpMatches = _deprecate(assertNotRegex)
+
+
class FunctionTestCase(TestCase):
"""A test case that wraps a test function.
diff --git a/Lib/unittest/loader.py b/Lib/unittest/loader.py
index eb18cd0..7e6ce2f 100644
--- a/Lib/unittest/loader.py
+++ b/Lib/unittest/loader.py
@@ -93,8 +93,30 @@ class TestLoader(object):
loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames))
return loaded_suite
- def loadTestsFromModule(self, module, *, pattern=None):
+ # XXX After Python 3.5, remove backward compatibility hacks for
+ # use_load_tests deprecation via *args and **kws. See issue 16662.
+ def loadTestsFromModule(self, module, *args, pattern=None, **kws):
"""Return a suite of all test cases contained in the given module"""
+ # This method used to take an undocumented and unofficial
+ # use_load_tests argument. For backward compatibility, we still
+ # accept the argument (which can also be the first position) but we
+ # ignore it and issue a deprecation warning if it's present.
+ if len(args) > 0 or 'use_load_tests' in kws:
+ warnings.warn('use_load_tests is deprecated and ignored',
+ DeprecationWarning)
+ kws.pop('use_load_tests', None)
+ if len(args) > 1:
+ # Complain about the number of arguments, but don't forget the
+ # required `module` argument.
+ complaint = len(args) + 1
+ raise TypeError('loadTestsFromModule() takes 1 positional argument but {} were given'.format(complaint))
+ if len(kws) != 0:
+ # Since the keyword arguments are unsorted (see PEP 468), just
+ # pick the alphabetically sorted first argument to complain about,
+ # if multiple were given. At least the error message will be
+ # predictable.
+ complaint = sorted(kws)[0]
+ raise TypeError("loadTestsFromModule() got an unexpected keyword argument '{}'".format(complaint))
tests = []
for name in dir(module):
obj = getattr(module, name)
diff --git a/Lib/unittest/runner.py b/Lib/unittest/runner.py
index 6678adb..cb452c7 100644
--- a/Lib/unittest/runner.py
+++ b/Lib/unittest/runner.py
@@ -200,6 +200,15 @@ class TextTestRunner(object):
if self.warnings:
# if self.warnings is set, use it to filter all the warnings
warnings.simplefilter(self.warnings)
+ # if the filter is 'default' or 'always', special-case the
+ # warnings from the deprecated unittest methods to show them
+ # no more than once per module, because they can be fairly
+ # noisy. The -Wd and -Wa flags can be used to bypass this
+ # only when self.warnings is None.
+ if self.warnings in ['default', 'always']:
+ warnings.filterwarnings('module',
+ category=DeprecationWarning,
+ message=r'Please use assert\w+ instead.')
startTime = time.perf_counter()
startTestRun = getattr(result, 'startTestRun', None)
if startTestRun is not None:
diff --git a/Lib/unittest/test/_test_warnings.py b/Lib/unittest/test/_test_warnings.py
index 08b846e..5cbfb53 100644
--- a/Lib/unittest/test/_test_warnings.py
+++ b/Lib/unittest/test/_test_warnings.py
@@ -18,6 +18,17 @@ def warnfun():
warnings.warn('rw', RuntimeWarning)
class TestWarnings(unittest.TestCase):
+ # unittest warnings will be printed at most once per type (max one message
+ # for the fail* methods, and one for the assert* methods)
+ def test_assert(self):
+ self.assertEquals(2+2, 4)
+ self.assertEquals(2*2, 4)
+ self.assertEquals(2**2, 4)
+
+ def test_fail(self):
+ self.failUnless(1)
+ self.failUnless(True)
+
def test_other_unittest(self):
self.assertAlmostEqual(2+2, 4)
self.assertNotAlmostEqual(4+4, 2)
diff --git a/Lib/unittest/test/test_assertions.py b/Lib/unittest/test/test_assertions.py
index 6557104..a0db3423 100644
--- a/Lib/unittest/test/test_assertions.py
+++ b/Lib/unittest/test/test_assertions.py
@@ -271,6 +271,15 @@ class TestLongMessage(unittest.TestCase):
r"\+ \{'key': 'value'\}$",
r"\+ \{'key': 'value'\} : oops$"])
+ def testAssertDictContainsSubset(self):
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", DeprecationWarning)
+
+ self.assertMessages('assertDictContainsSubset', ({'key': 'value'}, {}),
+ ["^Missing: 'key'$", "^oops$",
+ "^Missing: 'key'$",
+ "^Missing: 'key' : oops$"])
+
def testAssertMultiLineEqual(self):
self.assertMessages('assertMultiLineEqual', ("", "foo"),
[r"\+ foo$", "^oops$",
diff --git a/Lib/unittest/test/test_case.py b/Lib/unittest/test/test_case.py
index 067ec8d..f6cb997 100644
--- a/Lib/unittest/test/test_case.py
+++ b/Lib/unittest/test/test_case.py
@@ -698,6 +698,36 @@ class Test_TestCase(unittest.TestCase, TestEquality, TestHashing):
self.assertRaises(self.failureException, self.assertNotIn, 'cow',
animals)
+ def testAssertDictContainsSubset(self):
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", DeprecationWarning)
+
+ self.assertDictContainsSubset({}, {})
+ self.assertDictContainsSubset({}, {'a': 1})
+ self.assertDictContainsSubset({'a': 1}, {'a': 1})
+ self.assertDictContainsSubset({'a': 1}, {'a': 1, 'b': 2})
+ self.assertDictContainsSubset({'a': 1, 'b': 2}, {'a': 1, 'b': 2})
+
+ with self.assertRaises(self.failureException):
+ self.assertDictContainsSubset({1: "one"}, {})
+
+ with self.assertRaises(self.failureException):
+ self.assertDictContainsSubset({'a': 2}, {'a': 1})
+
+ with self.assertRaises(self.failureException):
+ self.assertDictContainsSubset({'c': 1}, {'a': 1})
+
+ with self.assertRaises(self.failureException):
+ self.assertDictContainsSubset({'a': 1, 'c': 1}, {'a': 1})
+
+ with self.assertRaises(self.failureException):
+ self.assertDictContainsSubset({'a': 1, 'c': 1}, {'a': 1})
+
+ one = ''.join(chr(i) for i in range(255))
+ # this used to cause a UnicodeDecodeError constructing the failure msg
+ with self.assertRaises(self.failureException):
+ self.assertDictContainsSubset({'foo': one}, {'foo': '\uFFFD'})
+
def testAssertEqual(self):
equal_pairs = [
((), ()),
@@ -1760,18 +1790,45 @@ test case
pass
self.assertIsNone(value)
- def testDeprecatedFailMethods(self):
- """Test that the deprecated fail* methods get removed in 3.11"""
+ def testDeprecatedMethodNames(self):
+ """
+ Test that the deprecated methods raise a DeprecationWarning. See #9424.
+ """
+ old = (
+ (self.failIfEqual, (3, 5)),
+ (self.assertNotEquals, (3, 5)),
+ (self.failUnlessEqual, (3, 3)),
+ (self.assertEquals, (3, 3)),
+ (self.failUnlessAlmostEqual, (2.0, 2.0)),
+ (self.assertAlmostEquals, (2.0, 2.0)),
+ (self.failIfAlmostEqual, (3.0, 5.0)),
+ (self.assertNotAlmostEquals, (3.0, 5.0)),
+ (self.failUnless, (True,)),
+ (self.assert_, (True,)),
+ (self.failUnlessRaises, (TypeError, lambda _: 3.14 + 'spam')),
+ (self.failIf, (False,)),
+ (self.assertDictContainsSubset, (dict(a=1, b=2), dict(a=1, b=2, c=3))),
+ (self.assertRaisesRegexp, (KeyError, 'foo', lambda: {}['foo'])),
+ (self.assertRegexpMatches, ('bar', 'bar')),
+ )
+ for meth, args in old:
+ with self.assertWarns(DeprecationWarning):
+ meth(*args)
+
+ # disable this test for now. When the version where the fail* methods will
+ # be removed is decided, re-enable it and update the version
+ def _testDeprecatedFailMethods(self):
+ """Test that the deprecated fail* methods get removed in 3.x"""
+ if sys.version_info[:2] < (3, 3):
+ return
deprecated_names = [
'failIfEqual', 'failUnlessEqual', 'failUnlessAlmostEqual',
'failIfAlmostEqual', 'failUnless', 'failUnlessRaises', 'failIf',
- 'assertNotEquals', 'assertEquals', 'assertAlmostEquals',
- 'assertNotAlmostEquals', 'assert_', 'assertDictContainsSubset',
- 'assertRaisesRegexp', 'assertRegexpMatches'
+ 'assertDictContainsSubset',
]
for deprecated_name in deprecated_names:
with self.assertRaises(AttributeError):
- getattr(self, deprecated_name)
+ getattr(self, deprecated_name) # remove these in 3.x
def testDeepcopy(self):
# Issue: 5660
diff --git a/Lib/unittest/test/test_loader.py b/Lib/unittest/test/test_loader.py
index fc0d46e..90e2081 100644
--- a/Lib/unittest/test/test_loader.py
+++ b/Lib/unittest/test/test_loader.py
@@ -5,6 +5,25 @@ 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 = []
+ 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
+ return wrapper
+
+
class Test_TestLoader(unittest.TestCase):
### Basic object tests
@@ -155,8 +174,9 @@ class Test_TestLoader(unittest.TestCase):
self.assertEqual(list(suite), reference)
- # Check that loadTestsFromModule honors a module
+ # 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):
@@ -175,13 +195,126 @@ 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('ignore')
+ 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 = []
+ 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_warnings(record=True) as w:
+ warnings.simplefilter('always')
+ 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
- # In Python 3.11, the undocumented and unofficial use_load_tests has
- # been removed.
- with self.assertRaises(TypeError):
- loader.loadTestsFromModule(m, False)
- with self.assertRaises(TypeError):
- loader.loadTestsFromModule(m, use_load_tests=False)
+ 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('ignore')
+ 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')
diff --git a/Lib/unittest/test/test_program.py b/Lib/unittest/test/test_program.py
index 687f629..fa9e629 100644
--- a/Lib/unittest/test/test_program.py
+++ b/Lib/unittest/test/test_program.py
@@ -460,14 +460,14 @@ class TestCommandLineArgs(unittest.TestCase):
return stderr.decode()
t = '_test_warnings'
- self.assertIn('Ran 5 tests', run_unittest([t]))
- self.assertIn('Ran 5 tests', run_unittest(['-k', 'TestWarnings', t]))
- self.assertIn('Ran 5 tests', run_unittest(['discover', '-p', '*_test*', '-k', 'TestWarnings']))
- self.assertIn('Ran 1 test ', run_unittest(['-k', 'f', t]))
- self.assertIn('Ran 5 tests', run_unittest(['-k', 't', t]))
- self.assertIn('Ran 2 tests', run_unittest(['-k', '*t', t]))
- self.assertIn('Ran 5 tests', run_unittest(['-k', '*test_warnings.*Warning*', t]))
- self.assertIn('Ran 1 test ', run_unittest(['-k', '*test_warnings.*warning*', t]))
+ self.assertIn('Ran 7 tests', run_unittest([t]))
+ self.assertIn('Ran 7 tests', run_unittest(['-k', 'TestWarnings', t]))
+ self.assertIn('Ran 7 tests', run_unittest(['discover', '-p', '*_test*', '-k', 'TestWarnings']))
+ self.assertIn('Ran 2 tests', run_unittest(['-k', 'f', t]))
+ self.assertIn('Ran 7 tests', run_unittest(['-k', 't', t]))
+ self.assertIn('Ran 3 tests', run_unittest(['-k', '*t', t]))
+ self.assertIn('Ran 7 tests', run_unittest(['-k', '*test_warnings.*Warning*', t]))
+ self.assertIn('Ran 1 test', run_unittest(['-k', '*test_warnings.*warning*', t]))
if __name__ == '__main__':
diff --git a/Lib/unittest/test/test_runner.py b/Lib/unittest/test/test_runner.py
index bcf7538..c487e9e 100644
--- a/Lib/unittest/test/test_runner.py
+++ b/Lib/unittest/test/test_runner.py
@@ -1150,6 +1150,8 @@ class Test_TextTestRunner(unittest.TestCase):
return [b.splitlines() for b in p.communicate()]
opts = dict(stdout=subprocess.PIPE, stderr=subprocess.PIPE,
cwd=os.path.dirname(__file__))
+ ae_msg = b'Please use assertEqual instead.'
+ at_msg = b'Please use assertTrue instead.'
# no args -> all the warnings are printed, unittest warnings only once
p = subprocess.Popen([sys.executable, '-E', '_test_warnings.py'], **opts)
@@ -1157,11 +1159,11 @@ class Test_TextTestRunner(unittest.TestCase):
out, err = get_parse_out_err(p)
self.assertIn(b'OK', err)
# check that the total number of warnings in the output is correct
- self.assertEqual(len(out), 10)
+ self.assertEqual(len(out), 12)
# check that the numbers of the different kind of warnings is correct
for msg in [b'dw', b'iw', b'uw']:
self.assertEqual(out.count(msg), 3)
- for msg in [b'rw']:
+ for msg in [ae_msg, at_msg, b'rw']:
self.assertEqual(out.count(msg), 1)
args_list = (
@@ -1188,9 +1190,11 @@ class Test_TextTestRunner(unittest.TestCase):
with p:
out, err = get_parse_out_err(p)
self.assertIn(b'OK', err)
- self.assertEqual(len(out), 12)
+ self.assertEqual(len(out), 14)
for msg in [b'dw', b'iw', b'uw', b'rw']:
self.assertEqual(out.count(msg), 3)
+ for msg in [ae_msg, at_msg]:
+ self.assertEqual(out.count(msg), 1)
def testStdErrLookedUpAtInstantiationTime(self):
# see issue 10786