Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit76632b8

Browse files
authored
gh-62432: unittest runner: Exit code 5 if no tests were run (#102051)
As discussed inhttps://discuss.python.org/t/unittest-fail-if-zero-tests-were-discovered/21498/7It is common for test runner misconfiguration to fail to find any tests,This should be an error.Fixes:#62432
1 parentdc3f975 commit76632b8

File tree

8 files changed

+64
-22
lines changed

8 files changed

+64
-22
lines changed

‎Doc/library/unittest.rst‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2281,7 +2281,8 @@ Loading and running tests
22812281

22822282
The *testRunner* argument can either be a test runner class or an already
22832283
created instance of it. By default ``main`` calls:func:`sys.exit` with
2284-
an exit code indicating success or failure of the tests run.
2284+
an exit code indicating success (0) or failure (1) of the tests run.
2285+
An exit code of 5 indicates that no tests were run.
22852286

22862287
The *testLoader* argument has to be a:class:`TestLoader` instance,
22872288
and defaults to:data:`defaultTestLoader`.

‎Lib/test/test_unittest/test_program.py‎

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,22 @@ def testExpectedFailure(self):
7171
deftestUnexpectedSuccess(self):
7272
pass
7373

74-
classFooBarLoader(unittest.TestLoader):
75-
"""Test loader that returns a suite containing FooBar."""
74+
classEmpty(unittest.TestCase):
75+
pass
76+
77+
classTestLoader(unittest.TestLoader):
78+
"""Test loader that returns a suite containing the supplied testcase."""
79+
80+
def__init__(self,testcase):
81+
self.testcase=testcase
82+
7683
defloadTestsFromModule(self,module):
7784
returnself.suiteClass(
78-
[self.loadTestsFromTestCase(Test_TestProgram.FooBar)])
85+
[self.loadTestsFromTestCase(self.testcase)])
7986

8087
defloadTestsFromNames(self,names,module):
8188
returnself.suiteClass(
82-
[self.loadTestsFromTestCase(Test_TestProgram.FooBar)])
89+
[self.loadTestsFromTestCase(self.testcase)])
8390

8491
deftest_defaultTest_with_string(self):
8592
classFakeRunner(object):
@@ -92,7 +99,7 @@ def run(self, test):
9299
runner=FakeRunner()
93100
program=unittest.TestProgram(testRunner=runner,exit=False,
94101
defaultTest='test.test_unittest',
95-
testLoader=self.FooBarLoader())
102+
testLoader=self.TestLoader(self.FooBar))
96103
sys.argv=old_argv
97104
self.assertEqual(('test.test_unittest',),program.testNames)
98105

@@ -108,7 +115,7 @@ def run(self, test):
108115
program=unittest.TestProgram(
109116
testRunner=runner,exit=False,
110117
defaultTest=['test.test_unittest','test.test_unittest2'],
111-
testLoader=self.FooBarLoader())
118+
testLoader=self.TestLoader(self.FooBar))
112119
sys.argv=old_argv
113120
self.assertEqual(['test.test_unittest','test.test_unittest2'],
114121
program.testNames)
@@ -118,7 +125,7 @@ def test_NonExit(self):
118125
program=unittest.main(exit=False,
119126
argv=["foobar"],
120127
testRunner=unittest.TextTestRunner(stream=stream),
121-
testLoader=self.FooBarLoader())
128+
testLoader=self.TestLoader(self.FooBar))
122129
self.assertTrue(hasattr(program,'result'))
123130
out=stream.getvalue()
124131
self.assertIn('\nFAIL: testFail ',out)
@@ -130,13 +137,13 @@ def test_NonExit(self):
130137

131138
deftest_Exit(self):
132139
stream=BufferedWriter()
133-
self.assertRaises(
134-
SystemExit,
135-
unittest.main,
136-
argv=["foobar"],
137-
testRunner=unittest.TextTestRunner(stream=stream),
138-
exit=True,
139-
testLoader=self.FooBarLoader())
140+
withself.assertRaises(SystemExit)ascm:
141+
unittest.main(
142+
argv=["foobar"],
143+
testRunner=unittest.TextTestRunner(stream=stream),
144+
exit=True,
145+
testLoader=self.TestLoader(self.FooBar))
146+
self.assertEqual(cm.exception.code,1)
140147
out=stream.getvalue()
141148
self.assertIn('\nFAIL: testFail ',out)
142149
self.assertIn('\nERROR: testError ',out)
@@ -147,12 +154,11 @@ def test_Exit(self):
147154

148155
deftest_ExitAsDefault(self):
149156
stream=BufferedWriter()
150-
self.assertRaises(
151-
SystemExit,
152-
unittest.main,
153-
argv=["foobar"],
154-
testRunner=unittest.TextTestRunner(stream=stream),
155-
testLoader=self.FooBarLoader())
157+
withself.assertRaises(SystemExit):
158+
unittest.main(
159+
argv=["foobar"],
160+
testRunner=unittest.TextTestRunner(stream=stream),
161+
testLoader=self.TestLoader(self.FooBar))
156162
out=stream.getvalue()
157163
self.assertIn('\nFAIL: testFail ',out)
158164
self.assertIn('\nERROR: testError ',out)
@@ -161,6 +167,17 @@ def test_ExitAsDefault(self):
161167
'expected failures=1, unexpected successes=1)\n')
162168
self.assertTrue(out.endswith(expected))
163169

170+
deftest_ExitEmptySuite(self):
171+
stream=BufferedWriter()
172+
withself.assertRaises(SystemExit)ascm:
173+
unittest.main(
174+
argv=["empty"],
175+
testRunner=unittest.TextTestRunner(stream=stream),
176+
testLoader=self.TestLoader(self.Empty))
177+
self.assertEqual(cm.exception.code,5)
178+
out=stream.getvalue()
179+
self.assertIn('\nNO TESTS RAN\n',out)
180+
164181

165182
classInitialisableProgram(unittest.TestProgram):
166183
exit=False

‎Lib/test/test_unittest/test_result.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@ def testFailFastSetByRunner(self):
451451
stream=BufferedWriter()
452452
runner=unittest.TextTestRunner(stream=stream,failfast=True)
453453
deftest(result):
454+
result.testsRun+=1
454455
self.assertTrue(result.failfast)
455456
result=runner.run(test)
456457
stream.flush()

‎Lib/test/test_unittest/test_runner.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,16 @@ def test(self):
577577
'inner setup','inner test','inner cleanup',
578578
'end outer test','outer cleanup'])
579579

580+
deftest_run_empty_suite_error_message(self):
581+
classEmptyTest(unittest.TestCase):
582+
pass
583+
584+
suite=unittest.defaultTestLoader.loadTestsFromTestCase(EmptyTest)
585+
runner=getRunner()
586+
runner.run(suite)
587+
588+
self.assertIn("\nNO TESTS RAN\n",runner.stream.getvalue())
589+
580590

581591
classTestModuleCleanUp(unittest.TestCase):
582592
deftest_add_and_do_ModuleCleanup(self):

‎Lib/unittest/main.py‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .signalsimportinstallHandler
1010

1111
__unittest=True
12+
_NO_TESTS_EXITCODE=5
1213

1314
MAIN_EXAMPLES="""\
1415
Examples:
@@ -279,6 +280,12 @@ def runTests(self):
279280
testRunner=self.testRunner
280281
self.result=testRunner.run(self.test)
281282
ifself.exit:
282-
sys.exit(notself.result.wasSuccessful())
283+
ifself.result.testsRun==0:
284+
sys.exit(_NO_TESTS_EXITCODE)
285+
elifself.result.wasSuccessful():
286+
sys.exit(0)
287+
else:
288+
sys.exit(1)
289+
283290

284291
main=TestProgram

‎Lib/unittest/runner.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ def run(self, test):
274274
infos.append("failures=%d"%failed)
275275
iferrored:
276276
infos.append("errors=%d"%errored)
277+
elifrun==0:
278+
self.stream.write("NO TESTS RAN")
277279
else:
278280
self.stream.write("OK")
279281
ifskipped:

‎Misc/ACKS‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,6 +1513,7 @@ Vlad Riscutia
15131513
Wes Rishel
15141514
Daniel Riti
15151515
Juan M. Bello Rivas
1516+
Stefano Rivera
15161517
Llandy Riveron Del Risco
15171518
Mohd Sanad Zaki Rizvi
15181519
Davide Rizzo
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The:mod:`unittest` runner will now exit with status code 5 if no tests
2+
were run. It is common for test runner misconfiguration to fail to find
3+
any tests, this should be an error.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp