diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index d413224fd49836..35f4364accd307 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -2012,13 +2012,16 @@ Loading and running tests .. method:: wasSuccessful() - Return ``True`` if all tests run so far have passed, otherwise returns - ``False``. + Return ``True`` if all tests run so far have passed and at least one test + was found, otherwise returns ``False``. .. versionchanged:: 3.4 Returns ``False`` if there were any :attr:`unexpectedSuccesses` from tests marked with the :func:`expectedFailure` decorator. + .. versionchanged:: 3.10b1 + Returns ``False`` if no tests were found. + .. method:: stop() This method can be called to signal that the set of tests being run should diff --git a/Lib/unittest/result.py b/Lib/unittest/result.py index ce7468e31481f0..7c7e3adce46da9 100644 --- a/Lib/unittest/result.py +++ b/Lib/unittest/result.py @@ -159,10 +159,15 @@ def addUnexpectedSuccess(self, test): def wasSuccessful(self): """Tells whether or not this result was a success.""" + # testsRun > 0 ensures that we do not return success if test + # discovery failed. We additionally need to check skipped list + # since class/module-level skips do not count towards testsRun. + # # The hasattr check is for test_result's OldResult test. That # way this method works on objects that lack the attribute. # (where would such result instances come from? old stored pickles?) - return ((len(self.failures) == len(self.errors) == 0) and + return ((self.testsRun > 0 or len(getattr(self, 'skipped', ())) > 0) and + (len(self.failures) == len(self.errors) == 0) and (not hasattr(self, 'unexpectedSuccesses') or len(self.unexpectedSuccesses) == 0)) diff --git a/Lib/unittest/runner.py b/Lib/unittest/runner.py index 45e7e4c0458d4c..edf81026514381 100644 --- a/Lib/unittest/runner.py +++ b/Lib/unittest/runner.py @@ -184,8 +184,11 @@ def run(self, test): if hasattr(result, 'separator2'): self.stream.writeln(result.separator2) run = result.testsRun - self.stream.writeln("Ran %d test%s in %.3fs" % - (run, run != 1 and "s" or "", timeTaken)) + if run > 0 or len(result.skipped) > 0: + self.stream.writeln("Ran %d test%s in %.3fs" % + (run, run != 1 and "s" or "", timeTaken)) + else: + self.stream.writeln("No tests found") self.stream.writeln() expectedFails = unexpectedSuccesses = skipped = 0 diff --git a/Lib/unittest/test/test_result.py b/Lib/unittest/test/test_result.py index a4af67bd8d56de..3bf94e28911f7b 100644 --- a/Lib/unittest/test/test_result.py +++ b/Lib/unittest/test/test_result.py @@ -6,6 +6,7 @@ import traceback import unittest +from unittest.suite import _ErrorHolder class MockTraceback(object): @@ -35,9 +36,10 @@ class Test_TestResult(unittest.TestCase): def test_init(self): result = unittest.TestResult() - self.assertTrue(result.wasSuccessful()) + self.assertFalse(result.wasSuccessful()) self.assertEqual(len(result.errors), 0) self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.skipped), 0) self.assertEqual(result.testsRun, 0) self.assertEqual(result.shouldStop, False) self.assertIsNone(result._stdout_buffer) @@ -69,6 +71,7 @@ def test_1(self): self.assertTrue(result.wasSuccessful()) self.assertEqual(len(result.errors), 0) self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.skipped), 0) self.assertEqual(result.testsRun, 1) self.assertEqual(result.shouldStop, False) @@ -90,6 +93,7 @@ def test_1(self): self.assertTrue(result.wasSuccessful()) self.assertEqual(len(result.errors), 0) self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.skipped), 0) self.assertEqual(result.testsRun, 1) self.assertEqual(result.shouldStop, False) @@ -143,6 +147,7 @@ def test_1(self): self.assertTrue(result.wasSuccessful()) self.assertEqual(len(result.errors), 0) self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.skipped), 0) self.assertEqual(result.testsRun, 1) self.assertEqual(result.shouldStop, False) @@ -186,6 +191,7 @@ def test_1(self): self.assertFalse(result.wasSuccessful()) self.assertEqual(len(result.errors), 0) self.assertEqual(len(result.failures), 1) + self.assertEqual(len(result.skipped), 0) self.assertEqual(result.testsRun, 1) self.assertEqual(result.shouldStop, False) @@ -234,6 +240,7 @@ def test_1(self): self.assertFalse(result.wasSuccessful()) self.assertEqual(len(result.errors), 1) self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.skipped), 0) self.assertEqual(result.testsRun, 1) self.assertEqual(result.shouldStop, False) @@ -260,6 +267,48 @@ def test_1(self): test_case, formatted_exc = result.errors[0] self.assertEqual('A tracebacklocals', formatted_exc) + def test_addSkip(self): + class Foo(unittest.TestCase): + def test_1(self): + pass + + test = Foo('test_1') + + result = unittest.TestResult() + + result.startTest(test) + result.addSkip(test, "skipped") + result.stopTest(test) + + self.assertTrue(result.wasSuccessful()) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.skipped), 1) + self.assertEqual(result.testsRun, 1) + self.assertEqual(result.shouldStop, False) + + test_case, reason = result.skipped[0] + self.assertIs(test_case, test) + self.assertEqual(reason, "skipped") + + def test_addSkipClassLevel(self): + """Test for SkipTest happening at the class level""" + error = _ErrorHolder('setUpClass (test_foo.Foo)') + + result = unittest.TestResult() + result.addSkip(error, "skipped") + + self.assertTrue(result.wasSuccessful()) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.skipped), 1) + self.assertEqual(result.testsRun, 0) + self.assertEqual(result.shouldStop, False) + + test_case, reason = result.skipped[0] + self.assertIs(test_case, error) + self.assertEqual(reason, "skipped") + def test_addSubTest(self): class Foo(unittest.TestCase): def test_1(self): @@ -284,6 +333,7 @@ def test_1(self): self.assertFalse(result.wasSuccessful()) self.assertEqual(len(result.errors), 1) self.assertEqual(len(result.failures), 1) + self.assertEqual(len(result.skipped), 0) self.assertEqual(result.testsRun, 1) self.assertEqual(result.shouldStop, False) diff --git a/Misc/NEWS.d/next/Library/2021-03-16-13-15-44.bpo-18232.0CSR5D.rst b/Misc/NEWS.d/next/Library/2021-03-16-13-15-44.bpo-18232.0CSR5D.rst new file mode 100644 index 00000000000000..70e9028b918fb2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-03-16-13-15-44.bpo-18232.0CSR5D.rst @@ -0,0 +1,2 @@ +:func:`unittest.TestResult.wasSuccessful` now returns False if no tests were +found.