[Python-checkins] r72191 - in python/trunk: Doc/library/unittest.rst Doc/whatsnew/2.7.rst Lib/test/test_unittest.py Lib/unittest.py

michael.foord python-checkins at python.org
Sat May 2 13:43:07 CEST 2009


Author: michael.foord
Date: Sat May  2 13:43:06 2009
New Revision: 72191

Log:
Adds an exit parameter to unittest.main(). If False main no longer
calls sys.exit.

Closes issue 3379.

Michael Foord
 



Modified:
   python/trunk/Doc/library/unittest.rst
   python/trunk/Doc/whatsnew/2.7.rst
   python/trunk/Lib/test/test_unittest.py
   python/trunk/Lib/unittest.py

Modified: python/trunk/Doc/library/unittest.rst
==============================================================================
--- python/trunk/Doc/library/unittest.rst	(original)
+++ python/trunk/Doc/library/unittest.rst	Sat May  2 13:43:06 2009
@@ -1364,7 +1364,7 @@
       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 @@
           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.

Modified: python/trunk/Doc/whatsnew/2.7.rst
==============================================================================
--- python/trunk/Doc/whatsnew/2.7.rst	(original)
+++ python/trunk/Doc/whatsnew/2.7.rst	Sat May  2 13:43:06 2009
@@ -477,6 +477,10 @@
     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`.)

Modified: python/trunk/Lib/test/test_unittest.py
==============================================================================
--- python/trunk/Lib/test/test_unittest.py	(original)
+++ python/trunk/Lib/test/test_unittest.py	Sat May  2 13:43:06 2009
@@ -9,9 +9,10 @@
 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 @@
                              "^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 @@
 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()

Modified: python/trunk/Lib/unittest.py
==============================================================================
--- python/trunk/Lib/unittest.py	(original)
+++ python/trunk/Lib/unittest.py	Sat May  2 13:43:06 2009
@@ -1015,7 +1015,7 @@
     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 @@
 """
     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 @@
             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 @@
         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)


More information about the Python-checkins mailing list