From 755be9b1505af591b9f2ee424a6525b6c2b65ce9 Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Tue, 29 Mar 2022 16:02:09 +0100 Subject: bpo-14265: Adds fully qualified test name to unittest output (GH-32138) Co-authored-by: Andrew Svetlov --- Doc/library/unittest.rst | 34 ++++++---- Lib/unittest/case.py | 2 +- Lib/unittest/test/test_result.py | 74 ++++++++++++---------- Misc/ACKS | 1 + .../2022-03-27-10-41-24.bpo-14265.OBMlAi.rst | 1 + 5 files changed, 63 insertions(+), 49 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-03-27-10-41-24.bpo-14265.OBMlAi.rst diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 06df8ce..9b8b75a 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -139,9 +139,9 @@ line, the above script produces an output that looks like this:: Passing the ``-v`` option to your test script will instruct :func:`unittest.main` to enable a higher level of verbosity, and produce the following output:: - test_isupper (__main__.TestStringMethods) ... ok - test_split (__main__.TestStringMethods) ... ok - test_upper (__main__.TestStringMethods) ... ok + test_isupper (__main__.TestStringMethods.test_isupper) ... ok + test_split (__main__.TestStringMethods.test_split) ... ok + test_upper (__main__.TestStringMethods.test_upper) ... ok ---------------------------------------------------------------------- Ran 3 tests in 0.001s @@ -565,10 +565,10 @@ Basic skipping looks like this:: This is the output of running the example above in verbose mode:: - test_format (__main__.MyTestCase) ... skipped 'not supported in this library version' - test_nothing (__main__.MyTestCase) ... skipped 'demonstrating skipping' - test_maybe_skipped (__main__.MyTestCase) ... skipped 'external resource not available' - test_windows_support (__main__.MyTestCase) ... skipped 'requires Windows' + test_format (__main__.MyTestCase.test_format) ... skipped 'not supported in this library version' + test_nothing (__main__.MyTestCase.test_nothing) ... skipped 'demonstrating skipping' + test_maybe_skipped (__main__.MyTestCase.test_maybe_skipped) ... skipped 'external resource not available' + test_windows_support (__main__.MyTestCase.test_windows_support) ... skipped 'requires Windows' ---------------------------------------------------------------------- Ran 4 tests in 0.005s @@ -661,27 +661,33 @@ For example, the following test:: will produce the following output:: ====================================================================== - FAIL: test_even (__main__.NumbersTest) (i=1) + FAIL: test_even (__main__.NumbersTest.test_even) (i=1) + Test that numbers between 0 and 5 are all even. ---------------------------------------------------------------------- Traceback (most recent call last): - File "subtests.py", line 32, in test_even + File "subtests.py", line 11, in test_even self.assertEqual(i % 2, 0) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ AssertionError: 1 != 0 ====================================================================== - FAIL: test_even (__main__.NumbersTest) (i=3) + FAIL: test_even (__main__.NumbersTest.test_even) (i=3) + Test that numbers between 0 and 5 are all even. ---------------------------------------------------------------------- Traceback (most recent call last): - File "subtests.py", line 32, in test_even + File "subtests.py", line 11, in test_even self.assertEqual(i % 2, 0) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ AssertionError: 1 != 0 ====================================================================== - FAIL: test_even (__main__.NumbersTest) (i=5) + FAIL: test_even (__main__.NumbersTest.test_even) (i=5) + Test that numbers between 0 and 5 are all even. ---------------------------------------------------------------------- Traceback (most recent call last): - File "subtests.py", line 32, in test_even + File "subtests.py", line 11, in test_even self.assertEqual(i % 2, 0) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ AssertionError: 1 != 0 Without using a subtest, execution would stop after the first failure, @@ -689,7 +695,7 @@ and the error would be less easy to diagnose because the value of ``i`` wouldn't be displayed:: ====================================================================== - FAIL: test_even (__main__.NumbersTest) + FAIL: test_even (__main__.NumbersTest.test_even) ---------------------------------------------------------------------- Traceback (most recent call last): File "subtests.py", line 32, in test_even diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index 0d55020..55770c0 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -478,7 +478,7 @@ class TestCase(object): return hash((type(self), self._testMethodName)) def __str__(self): - return "%s (%s)" % (self._testMethodName, strclass(self.__class__)) + return "%s (%s.%s)" % (self._testMethodName, strclass(self.__class__), self._testMethodName) def __repr__(self): return "<%s testMethod=%s>" % \ diff --git a/Lib/unittest/test/test_result.py b/Lib/unittest/test/test_result.py index c616f28..b0cc3d8 100644 --- a/Lib/unittest/test/test_result.py +++ b/Lib/unittest/test/test_result.py @@ -423,7 +423,7 @@ class Test_TextTestResult(unittest.TestCase): self.assertEqual( result.getDescription(self), 'testGetDescriptionWithoutDocstring (' + __name__ + - '.Test_TextTestResult)') + '.Test_TextTestResult.testGetDescriptionWithoutDocstring)') def testGetSubTestDescriptionWithoutDocstring(self): with self.subTest(foo=1, bar=2): @@ -431,13 +431,14 @@ class Test_TextTestResult(unittest.TestCase): self.assertEqual( result.getDescription(self._subtest), 'testGetSubTestDescriptionWithoutDocstring (' + __name__ + - '.Test_TextTestResult) (foo=1, bar=2)') + '.Test_TextTestResult.testGetSubTestDescriptionWithoutDocstring) (foo=1, bar=2)') + with self.subTest('some message'): result = unittest.TextTestResult(None, True, 1) self.assertEqual( result.getDescription(self._subtest), 'testGetSubTestDescriptionWithoutDocstring (' + __name__ + - '.Test_TextTestResult) [some message]') + '.Test_TextTestResult.testGetSubTestDescriptionWithoutDocstring) [some message]') def testGetSubTestDescriptionWithoutDocstringAndParams(self): with self.subTest(): @@ -445,10 +446,11 @@ class Test_TextTestResult(unittest.TestCase): self.assertEqual( result.getDescription(self._subtest), 'testGetSubTestDescriptionWithoutDocstringAndParams ' - '(' + __name__ + '.Test_TextTestResult) ()') + '(' + __name__ + '.Test_TextTestResult.testGetSubTestDescriptionWithoutDocstringAndParams) ' + '()') def testGetSubTestDescriptionForFalsyValues(self): - expected = 'testGetSubTestDescriptionForFalsyValues (%s.Test_TextTestResult) [%s]' + expected = 'testGetSubTestDescriptionForFalsyValues (%s.Test_TextTestResult.testGetSubTestDescriptionForFalsyValues) [%s]' result = unittest.TextTestResult(None, True, 1) for arg in [0, None, []]: with self.subTest(arg): @@ -464,7 +466,8 @@ class Test_TextTestResult(unittest.TestCase): self.assertEqual( result.getDescription(self._subtest), 'testGetNestedSubTestDescriptionWithoutDocstring ' - '(' + __name__ + '.Test_TextTestResult) (baz=2, bar=3, foo=1)') + '(' + __name__ + '.Test_TextTestResult.testGetNestedSubTestDescriptionWithoutDocstring) ' + '(baz=2, bar=3, foo=1)') def testGetDuplicatedNestedSubTestDescriptionWithoutDocstring(self): with self.subTest(foo=1, bar=2): @@ -473,7 +476,7 @@ class Test_TextTestResult(unittest.TestCase): self.assertEqual( result.getDescription(self._subtest), 'testGetDuplicatedNestedSubTestDescriptionWithoutDocstring ' - '(' + __name__ + '.Test_TextTestResult) (baz=3, bar=4, foo=1)') + '(' + __name__ + '.Test_TextTestResult.testGetDuplicatedNestedSubTestDescriptionWithoutDocstring) (baz=3, bar=4, foo=1)') @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -483,7 +486,7 @@ class Test_TextTestResult(unittest.TestCase): self.assertEqual( result.getDescription(self), ('testGetDescriptionWithOneLineDocstring ' - '(' + __name__ + '.Test_TextTestResult)\n' + '(' + __name__ + '.Test_TextTestResult.testGetDescriptionWithOneLineDocstring)\n' 'Tests getDescription() for a method with a docstring.')) @unittest.skipIf(sys.flags.optimize >= 2, @@ -495,7 +498,9 @@ class Test_TextTestResult(unittest.TestCase): self.assertEqual( result.getDescription(self._subtest), ('testGetSubTestDescriptionWithOneLineDocstring ' - '(' + __name__ + '.Test_TextTestResult) (foo=1, bar=2)\n' + '(' + __name__ + '.Test_TextTestResult.testGetSubTestDescriptionWithOneLineDocstring) ' + '(foo=1, bar=2)\n' + 'Tests getDescription() for a method with a docstring.')) @unittest.skipIf(sys.flags.optimize >= 2, @@ -508,7 +513,7 @@ class Test_TextTestResult(unittest.TestCase): self.assertEqual( result.getDescription(self), ('testGetDescriptionWithMultiLineDocstring ' - '(' + __name__ + '.Test_TextTestResult)\n' + '(' + __name__ + '.Test_TextTestResult.testGetDescriptionWithMultiLineDocstring)\n' 'Tests getDescription() for a method with a longer ' 'docstring.')) @@ -523,7 +528,8 @@ class Test_TextTestResult(unittest.TestCase): self.assertEqual( result.getDescription(self._subtest), ('testGetSubTestDescriptionWithMultiLineDocstring ' - '(' + __name__ + '.Test_TextTestResult) (foo=1, bar=2)\n' + '(' + __name__ + '.Test_TextTestResult.testGetSubTestDescriptionWithMultiLineDocstring) ' + '(foo=1, bar=2)\n' 'Tests getDescription() for a method with a longer ' 'docstring.')) @@ -582,17 +588,17 @@ class Test_TextTestResult(unittest.TestCase): def testLongOutput(self): classname = f'{__name__}.{self.Test.__qualname__}' self.assertEqual(self._run_test('testSuccess', 2), - f'testSuccess ({classname}) ... ok\n') + f'testSuccess ({classname}.testSuccess) ... ok\n') self.assertEqual(self._run_test('testSkip', 2), - f"testSkip ({classname}) ... skipped 'skip'\n") + f"testSkip ({classname}.testSkip) ... skipped 'skip'\n") self.assertEqual(self._run_test('testFail', 2), - f'testFail ({classname}) ... FAIL\n') + f'testFail ({classname}.testFail) ... FAIL\n') self.assertEqual(self._run_test('testError', 2), - f'testError ({classname}) ... ERROR\n') + f'testError ({classname}.testError) ... ERROR\n') self.assertEqual(self._run_test('testExpectedFailure', 2), - f'testExpectedFailure ({classname}) ... expected failure\n') + f'testExpectedFailure ({classname}.testExpectedFailure) ... expected failure\n') self.assertEqual(self._run_test('testUnexpectedSuccess', 2), - f'testUnexpectedSuccess ({classname}) ... unexpected success\n') + f'testUnexpectedSuccess ({classname}.testUnexpectedSuccess) ... unexpected success\n') def testDotsOutputSubTestSuccess(self): self.assertEqual(self._run_test('testSubTestSuccess', 1), '.') @@ -600,7 +606,7 @@ class Test_TextTestResult(unittest.TestCase): def testLongOutputSubTestSuccess(self): classname = f'{__name__}.{self.Test.__qualname__}' self.assertEqual(self._run_test('testSubTestSuccess', 2), - f'testSubTestSuccess ({classname}) ... ok\n') + f'testSubTestSuccess ({classname}.testSubTestSuccess) ... ok\n') def testDotsOutputSubTestMixed(self): self.assertEqual(self._run_test('testSubTestMixed', 1), 'sFE') @@ -608,10 +614,10 @@ class Test_TextTestResult(unittest.TestCase): def testLongOutputSubTestMixed(self): classname = f'{__name__}.{self.Test.__qualname__}' self.assertEqual(self._run_test('testSubTestMixed', 2), - f'testSubTestMixed ({classname}) ... \n' - f" testSubTestMixed ({classname}) [skip] (b=2) ... skipped 'skip'\n" - f' testSubTestMixed ({classname}) [fail] (c=3) ... FAIL\n' - f' testSubTestMixed ({classname}) [error] (d=4) ... ERROR\n') + f'testSubTestMixed ({classname}.testSubTestMixed) ... \n' + f" testSubTestMixed ({classname}.testSubTestMixed) [skip] (b=2) ... skipped 'skip'\n" + f' testSubTestMixed ({classname}.testSubTestMixed) [fail] (c=3) ... FAIL\n' + f' testSubTestMixed ({classname}.testSubTestMixed) [error] (d=4) ... ERROR\n') def testDotsOutputTearDownFail(self): out = self._run_test('testSuccess', 1, AssertionError('fail')) @@ -627,19 +633,19 @@ class Test_TextTestResult(unittest.TestCase): classname = f'{__name__}.{self.Test.__qualname__}' out = self._run_test('testSuccess', 2, AssertionError('fail')) self.assertEqual(out, - f'testSuccess ({classname}) ... FAIL\n') + f'testSuccess ({classname}.testSuccess) ... FAIL\n') out = self._run_test('testError', 2, AssertionError('fail')) self.assertEqual(out, - f'testError ({classname}) ... ERROR\n' - f'testError ({classname}) ... FAIL\n') + f'testError ({classname}.testError) ... ERROR\n' + f'testError ({classname}.testError) ... FAIL\n') out = self._run_test('testFail', 2, Exception('error')) self.assertEqual(out, - f'testFail ({classname}) ... FAIL\n' - f'testFail ({classname}) ... ERROR\n') + f'testFail ({classname}.testFail) ... FAIL\n' + f'testFail ({classname}.testFail) ... ERROR\n') out = self._run_test('testSkip', 2, AssertionError('fail')) self.assertEqual(out, - f"testSkip ({classname}) ... skipped 'skip'\n" - f'testSkip ({classname}) ... FAIL\n') + f"testSkip ({classname}.testSkip) ... skipped 'skip'\n" + f'testSkip ({classname}.testSkip) ... FAIL\n') classDict = dict(unittest.TestResult.__dict__) @@ -852,7 +858,7 @@ class TestOutputBuffering(unittest.TestCase): expected_out = '\nStdout:\nset up\n' self.assertEqual(stdout.getvalue(), expected_out) self.assertEqual(len(result.errors), 1) - description = f'test_foo ({strclass(Foo)})' + description = f'test_foo ({strclass(Foo)}.test_foo)' test_case, formatted_exc = result.errors[0] self.assertEqual(str(test_case), description) self.assertIn('ZeroDivisionError: division by zero', formatted_exc) @@ -874,7 +880,7 @@ class TestOutputBuffering(unittest.TestCase): expected_out = '\nStdout:\ntear down\n' self.assertEqual(stdout.getvalue(), expected_out) self.assertEqual(len(result.errors), 1) - description = f'test_foo ({strclass(Foo)})' + description = f'test_foo ({strclass(Foo)}.test_foo)' test_case, formatted_exc = result.errors[0] self.assertEqual(str(test_case), description) self.assertIn('ZeroDivisionError: division by zero', formatted_exc) @@ -897,7 +903,7 @@ class TestOutputBuffering(unittest.TestCase): expected_out = '\nStdout:\nset up\ndo cleanup2\ndo cleanup1\n' self.assertEqual(stdout.getvalue(), expected_out) self.assertEqual(len(result.errors), 2) - description = f'test_foo ({strclass(Foo)})' + description = f'test_foo ({strclass(Foo)}.test_foo)' test_case, formatted_exc = result.errors[0] self.assertEqual(str(test_case), description) self.assertIn('ValueError: bad cleanup2', formatted_exc) @@ -928,7 +934,7 @@ class TestOutputBuffering(unittest.TestCase): expected_out = '\nStdout:\nset up\ndo cleanup2\ndo cleanup1\n' self.assertEqual(stdout.getvalue(), expected_out) self.assertEqual(len(result.errors), 3) - description = f'test_foo ({strclass(Foo)})' + description = f'test_foo ({strclass(Foo)}.test_foo)' test_case, formatted_exc = result.errors[0] self.assertEqual(str(test_case), description) self.assertIn('ZeroDivisionError: division by zero', formatted_exc) @@ -971,7 +977,7 @@ class TestOutputBuffering(unittest.TestCase): expected_out = '\nStdout:\nset up\ntear down\ndo cleanup2\ndo cleanup1\n' self.assertEqual(stdout.getvalue(), expected_out) self.assertEqual(len(result.errors), 3) - description = f'test_foo ({strclass(Foo)})' + description = f'test_foo ({strclass(Foo)}.test_foo)' test_case, formatted_exc = result.errors[0] self.assertEqual(str(test_case), description) self.assertIn('ZeroDivisionError: division by zero', formatted_exc) diff --git a/Misc/ACKS b/Misc/ACKS index efa2474..72e2cfe 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -516,6 +516,7 @@ Daniel Evers evilzero Winston Ewert Greg Ewing +Sam Ezeh Martijn Faassen Clovis Fabricio Andreas Faerber diff --git a/Misc/NEWS.d/next/Library/2022-03-27-10-41-24.bpo-14265.OBMlAi.rst b/Misc/NEWS.d/next/Library/2022-03-27-10-41-24.bpo-14265.OBMlAi.rst new file mode 100644 index 0000000..308ce36 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-03-27-10-41-24.bpo-14265.OBMlAi.rst @@ -0,0 +1 @@ +Adds the fully qualified test name to unittest output -- cgit v0.12