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:
parent
27f204dc29
commit
829f6b8052
|
@ -1364,7 +1364,7 @@ Loading and running tests
|
||||||
subclasses to provide a custom ``TestResult``.
|
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
|
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
|
test modules conveniently executable. The simplest use for this function is to
|
||||||
|
@ -1374,4 +1374,18 @@ Loading and running tests
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
The *testRunner* argument can either be a test runner class or an already
|
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.
|
||||||
|
|
|
@ -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
|
to provide additional information about why the two objects are
|
||||||
matching, much as the new sequence comparison methods do.
|
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
|
* 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
|
accept a file object, in addition to the path names accepted in earlier
|
||||||
versions. (Contributed by Gabriel Genellina; :issue:`4756`.)
|
versions. (Contributed by Gabriel Genellina; :issue:`4756`.)
|
||||||
|
|
|
@ -9,9 +9,10 @@ Still need testing:
|
||||||
import re
|
import re
|
||||||
from test import test_support
|
from test import test_support
|
||||||
import unittest
|
import unittest
|
||||||
from unittest import TestCase
|
from unittest import TestCase, TestProgram
|
||||||
import types
|
import types
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
from cStringIO import StringIO
|
||||||
|
|
||||||
### Support code
|
### Support code
|
||||||
################################################################
|
################################################################
|
||||||
|
@ -3040,6 +3041,73 @@ class TestLongMessage(TestCase):
|
||||||
"^unexpectedly identical: None : oops$"])
|
"^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
|
## Main
|
||||||
######################################################################
|
######################################################################
|
||||||
|
@ -3047,7 +3115,8 @@ class TestLongMessage(TestCase):
|
||||||
def test_main():
|
def test_main():
|
||||||
test_support.run_unittest(Test_TestCase, Test_TestLoader,
|
test_support.run_unittest(Test_TestCase, Test_TestLoader,
|
||||||
Test_TestSuite, Test_TestResult, Test_FunctionTestCase,
|
Test_TestSuite, Test_TestResult, Test_FunctionTestCase,
|
||||||
Test_TestSkipping, Test_Assertions, TestLongMessage)
|
Test_TestSkipping, Test_Assertions, TestLongMessage,
|
||||||
|
Test_TestProgram)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_main()
|
test_main()
|
||||||
|
|
|
@ -1015,7 +1015,7 @@ class TestSuite(object):
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if not isinstance(other, self.__class__):
|
if not isinstance(other, self.__class__):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
return self._tests == other._tests
|
return list(self) == list(other)
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not self == other
|
return not self == other
|
||||||
|
@ -1469,7 +1469,7 @@ Examples:
|
||||||
"""
|
"""
|
||||||
def __init__(self, module='__main__', defaultTest=None,
|
def __init__(self, module='__main__', defaultTest=None,
|
||||||
argv=None, testRunner=TextTestRunner,
|
argv=None, testRunner=TextTestRunner,
|
||||||
testLoader=defaultTestLoader):
|
testLoader=defaultTestLoader, exit=True):
|
||||||
if isinstance(module, basestring):
|
if isinstance(module, basestring):
|
||||||
self.module = __import__(module)
|
self.module = __import__(module)
|
||||||
for part in module.split('.')[1:]:
|
for part in module.split('.')[1:]:
|
||||||
|
@ -1478,6 +1478,8 @@ Examples:
|
||||||
self.module = module
|
self.module = module
|
||||||
if argv is None:
|
if argv is None:
|
||||||
argv = sys.argv
|
argv = sys.argv
|
||||||
|
|
||||||
|
self.exit = exit
|
||||||
self.verbosity = 1
|
self.verbosity = 1
|
||||||
self.defaultTest = defaultTest
|
self.defaultTest = defaultTest
|
||||||
self.testRunner = testRunner
|
self.testRunner = testRunner
|
||||||
|
@ -1529,15 +1531,12 @@ Examples:
|
||||||
else:
|
else:
|
||||||
# it is assumed to be a TestRunner instance
|
# it is assumed to be a TestRunner instance
|
||||||
testRunner = self.testRunner
|
testRunner = self.testRunner
|
||||||
result = testRunner.run(self.test)
|
self.result = testRunner.run(self.test)
|
||||||
sys.exit(not result.wasSuccessful())
|
if self.exit:
|
||||||
|
sys.exit(not self.result.wasSuccessful())
|
||||||
|
|
||||||
main = TestProgram
|
main = TestProgram
|
||||||
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
# Executing this module from the command line
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main(module=None)
|
main(module=None)
|
||||||
|
|
Loading…
Reference in New Issue