diff options
author | Gregory P. Smith <greg@krypto.org> | 2022-01-27 04:39:15 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-27 04:39:15 (GMT) |
commit | b50322d20337ca468f2070eedb051a16ee1eba94 (patch) | |
tree | ba478b8fe2b91b583cec640d518f9643fb5b6f7d /Lib/unittest/test | |
parent | 9f0881476e0113d3a35e0ffa96649b9276dd75c5 (diff) | |
download | cpython-b50322d20337ca468f2070eedb051a16ee1eba94.zip cpython-b50322d20337ca468f2070eedb051a16ee1eba94.tar.gz cpython-b50322d20337ca468f2070eedb051a16ee1eba94.tar.bz2 |
bpo-45162: Revert "Remove many old deprecated unittest features" (GH-30935)
Revert "bpo-45162: Remove many old deprecated unittest features (GH-28268)"
This reverts commit b0a6ede3d0bd6fa4ffe413ab4dfc1059201df25b.
We're deferring this change until 3.12 while upstream projects that use
the legacy assertion method names are fixed. See the issue for links
to the discussion. Many upstream projects now have issues and PRs
filed.
Diffstat (limited to 'Lib/unittest/test')
-rw-r--r-- | Lib/unittest/test/_test_warnings.py | 11 | ||||
-rw-r--r-- | Lib/unittest/test/test_assertions.py | 9 | ||||
-rw-r--r-- | Lib/unittest/test/test_case.py | 69 | ||||
-rw-r--r-- | Lib/unittest/test/test_loader.py | 147 | ||||
-rw-r--r-- | Lib/unittest/test/test_program.py | 16 | ||||
-rw-r--r-- | Lib/unittest/test/test_runner.py | 10 |
6 files changed, 238 insertions, 24 deletions
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 |