Adds an exit parameter to unittest.main(). If False main no longer

calls sys.exit.

Closes issue 3379.

Michael Foord
This commit is contained in:
Michael Foord 2009-05-02 11:43:06 +00:00
parent 27f204dc29
commit 829f6b8052
4 changed files with 98 additions and 12 deletions

View File

@ -1364,7 +1364,7 @@ Loading and running tests
subclasses to provide a custom ``TestResult``.
.. function:: main([module[, defaultTest[, argv[, testRunner[, testLoader]]]]])
.. function:: main([module[, defaultTest[, argv[, testRunner[, testLoader[, exit]]]]]])
A command-line program that runs a set of tests; this is primarily for making
test modules conveniently executable. The simplest use for this function is to
@ -1374,4 +1374,18 @@ Loading and running tests
unittest.main()
The *testRunner* argument can either be a test runner class or an already
created instance of it.
created instance of it. By default ``main`` calls :func:`sys.exit` with
an exit code indicating success or failure of the tests run.
``main`` supports being used from the interactive interpreter by passing in the
argument ``exit=False``. This displays the result on standard output without
calling :func:`sys.exit`::
>>> from unittest import main
>>> main(module='test_module', exit=False)
Calling ``main`` actually returns an instance of the ``TestProgram`` class.
This stores the result of the tests run as the ``result`` attribute.
.. versionchanged:: 2.7
The ``exit`` parameter was added.

View File

@ -477,6 +477,10 @@ changes, or look through the Subversion logs for all the details.
to provide additional information about why the two objects are
matching, much as the new sequence comparison methods do.
:func:`unittest.main` now takes an optional ``exit`` argument.
If False ``main`` doesn't call :func:`sys.exit` allowing it to
be used from the interactive interpreter. :issue:`3379`.
* The :func:`is_zipfile` function in the :mod:`zipfile` module will now
accept a file object, in addition to the path names accepted in earlier
versions. (Contributed by Gabriel Genellina; :issue:`4756`.)

View File

@ -9,9 +9,10 @@ Still need testing:
import re
from test import test_support
import unittest
from unittest import TestCase
from unittest import TestCase, TestProgram
import types
from copy import deepcopy
from cStringIO import StringIO
### Support code
################################################################
@ -3040,6 +3041,73 @@ class TestLongMessage(TestCase):
"^unexpectedly identical: None : oops$"])
class Test_TestProgram(TestCase):
# Horrible white box test
def testNoExit(self):
result = object()
test = object()
class FakeRunner(object):
def run(self, test):
self.test = test
return result
runner = FakeRunner()
try:
oldParseArgs = TestProgram.parseArgs
TestProgram.parseArgs = lambda *args: None
TestProgram.test = test
program = TestProgram(testRunner=runner, exit=False)
self.assertEqual(program.result, result)
self.assertEqual(runner.test, test)
finally:
TestProgram.parseArgs = oldParseArgs
del TestProgram.test
class FooBar(unittest.TestCase):
def testPass(self):
assert True
def testFail(self):
assert False
class FooBarLoader(unittest.TestLoader):
"""Test loader that returns a suite containing FooBar."""
def loadTestsFromModule(self, module):
return self.suiteClass(
[self.loadTestsFromTestCase(Test_TestProgram.FooBar)])
def test_NonExit(self):
program = unittest.main(exit=False,
testRunner=unittest.TextTestRunner(stream=StringIO()),
testLoader=self.FooBarLoader())
self.assertTrue(hasattr(program, 'result'))
def test_Exit(self):
self.assertRaises(
SystemExit,
unittest.main,
testRunner=unittest.TextTestRunner(stream=StringIO()),
exit=True,
testLoader=self.FooBarLoader())
def test_ExitAsDefault(self):
self.assertRaises(
SystemExit,
unittest.main,
testRunner=unittest.TextTestRunner(stream=StringIO()),
testLoader=self.FooBarLoader())
######################################################################
## Main
######################################################################
@ -3047,7 +3115,8 @@ class TestLongMessage(TestCase):
def test_main():
test_support.run_unittest(Test_TestCase, Test_TestLoader,
Test_TestSuite, Test_TestResult, Test_FunctionTestCase,
Test_TestSkipping, Test_Assertions, TestLongMessage)
Test_TestSkipping, Test_Assertions, TestLongMessage,
Test_TestProgram)
if __name__ == "__main__":
test_main()

View File

@ -1015,7 +1015,7 @@ class TestSuite(object):
def __eq__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
return self._tests == other._tests
return list(self) == list(other)
def __ne__(self, other):
return not self == other
@ -1469,7 +1469,7 @@ Examples:
"""
def __init__(self, module='__main__', defaultTest=None,
argv=None, testRunner=TextTestRunner,
testLoader=defaultTestLoader):
testLoader=defaultTestLoader, exit=True):
if isinstance(module, basestring):
self.module = __import__(module)
for part in module.split('.')[1:]:
@ -1478,6 +1478,8 @@ Examples:
self.module = module
if argv is None:
argv = sys.argv
self.exit = exit
self.verbosity = 1
self.defaultTest = defaultTest
self.testRunner = testRunner
@ -1529,15 +1531,12 @@ Examples:
else:
# it is assumed to be a TestRunner instance
testRunner = self.testRunner
result = testRunner.run(self.test)
sys.exit(not result.wasSuccessful())
self.result = testRunner.run(self.test)
if self.exit:
sys.exit(not self.result.wasSuccessful())
main = TestProgram
##############################################################################
# Executing this module from the command line
##############################################################################
if __name__ == "__main__":
main(module=None)