Index: Doc/library/unittest.rst =================================================================== --- Doc/library/unittest.rst (revision 59056) +++ Doc/library/unittest.rst (arbetskopia) @@ -765,6 +765,14 @@ Contains formatted tracebacks instead of :func:`sys.exc_info` results. +.. attribute:: TestResult.notImplemented + + A list containing :class:`TestCase` instances for those tests that raised + a :class:`NotImplementedError`. + + .. versionadded:: 2.6 + + .. attribute:: TestResult.testsRun The total number of tests run so far. @@ -773,8 +781,11 @@ .. method:: TestResult.wasSuccessful() Returns :const:`True` if all tests run so far have passed, otherwise returns - :const:`False`. + :const:`False`. For this method, a test that is raises + :class:`NotImplementedError` is considered to have passed. + .. versionchanged:: 2.6 + Modified to ignore tests that raise :class:`NotImplementedError`. .. method:: TestResult.stop() @@ -834,6 +845,19 @@ The default implementation does nothing. +.. method:: TestResult.addNotImplemented(test) + + Called when the test case *test* raises :class:`NotImplementedError`. + A test case that raises :class:`NotImplementedError` + is not a failure. It indicates that part of the functionality has not + yet been implemented. + + The default implementation appends ``test`` to the instanceĀ“s + ``notImplemented`` atttribute. + + .. versionadded:: 2.6 + + .. _testloader-objects: TestLoader Objects Index: Lib/unittest.py =================================================================== --- Lib/unittest.py (revision 59056) +++ Lib/unittest.py (arbetskopia) @@ -106,6 +106,7 @@ def __init__(self): self.failures = [] self.errors = [] + self.notImplemented = [] self.testsRun = 0 self.shouldStop = False @@ -128,6 +129,10 @@ returned by sys.exc_info().""" self.failures.append((test, self._exc_info_to_string(err, test))) + def addNotImplemented(self, test): + """Called when a test raises NotImplementedError""" + self.notImplemented.append(test) + def addSuccess(self, test): "Called when a test has completed successfully" pass @@ -163,9 +168,9 @@ return length def __repr__(self): - return "<%s run=%i errors=%i failures=%i>" % \ + return "<%s run=%i errors=%i failures=%i not implemented=%i>" % \ (_strclass(self.__class__), self.testsRun, len(self.errors), - len(self.failures)) + len(self.failures), len(self.notImplemented)) class TestCase: """A class whose instances are single test cases. @@ -271,6 +276,8 @@ try: testMethod() ok = True + except NotImplementedError: + result.addNotImplemented(self) except self.failureException: result.addFailure(self, self._exc_info()) except KeyboardInterrupt: @@ -704,6 +711,13 @@ elif self.dots: self.stream.write('F') + def addNotImplemented(self, test): + TestResult.addNotImplemented(self, test) + if self.showAll: + self.stream.writeln("NOT IMPLEMENTED") + elif self.dots: + self.stream.write('-') + def printErrors(self): if self.dots or self.showAll: self.stream.writeln() @@ -745,15 +759,20 @@ self.stream.writeln("Ran %d test%s in %.3fs" % (run, run != 1 and "s" or "", timeTaken)) self.stream.writeln() + failed, errored, notImplemented = map(len, (result.failures, result.errors, result.notImplemented)) if not result.wasSuccessful(): self.stream.write("FAILED (") - failed, errored = map(len, (result.failures, result.errors)) if failed: self.stream.write("failures=%d" % failed) if errored: if failed: self.stream.write(", ") self.stream.write("errors=%d" % errored) + if notImplemented: + if failed or errored: self.stream.write(", ") + self.stream.write("not implemented=%d" % notImplemented) self.stream.writeln(")") + elif result.notImplemented: + self.stream.writeln("OK (but not implemented=%d)" % notImplemented) else: self.stream.writeln("OK") return result Index: Lib/test/test_doctest.py =================================================================== --- Lib/test/test_doctest.py (revision 59056) +++ Lib/test/test_doctest.py (arbetskopia) @@ -1804,19 +1804,19 @@ >>> import test.sample_doctest >>> suite = doctest.DocTestSuite(test.sample_doctest) >>> suite.run(unittest.TestResult()) - + We can also supply the module by name: >>> suite = doctest.DocTestSuite('test.sample_doctest') >>> suite.run(unittest.TestResult()) - + We can use the current module: >>> suite = test.sample_doctest.test_suite() >>> suite.run(unittest.TestResult()) - + We can supply global variables. If we pass globs, they will be used instead of the module globals. Here we'll pass an empty @@ -1824,7 +1824,7 @@ >>> suite = doctest.DocTestSuite('test.sample_doctest', globs={}) >>> suite.run(unittest.TestResult()) - + Alternatively, we can provide extra globals. Here we'll make an error go away by providing an extra global variable: @@ -1832,7 +1832,7 @@ >>> suite = doctest.DocTestSuite('test.sample_doctest', ... extraglobs={'y': 1}) >>> suite.run(unittest.TestResult()) - + You can pass option flags. Here we'll cause an extra error by disabling the blank-line feature: @@ -1840,7 +1840,7 @@ >>> suite = doctest.DocTestSuite('test.sample_doctest', ... optionflags=doctest.DONT_ACCEPT_BLANKLINE) >>> suite.run(unittest.TestResult()) - + You can supply setUp and tearDown functions: @@ -1857,7 +1857,7 @@ >>> suite = doctest.DocTestSuite('test.sample_doctest', ... setUp=setUp, tearDown=tearDown) >>> suite.run(unittest.TestResult()) - + But the tearDown restores sanity: @@ -1875,7 +1875,7 @@ >>> suite = doctest.DocTestSuite('test.sample_doctest', setUp=setUp) >>> suite.run(unittest.TestResult()) - + Here, we didn't need to use a tearDown function because we modified the test globals, which are a copy of the @@ -1894,7 +1894,7 @@ ... 'test_doctest2.txt', ... 'test_doctest4.txt') >>> suite.run(unittest.TestResult()) - + The test files are looked for in the directory containing the calling module. A package keyword argument can be provided to @@ -1906,14 +1906,14 @@ ... 'test_doctest4.txt', ... package='test') >>> suite.run(unittest.TestResult()) - + '/' should be used as a path separator. It will be converted to a native separator at run time: >>> suite = doctest.DocFileSuite('../test/test_doctest.txt') >>> suite.run(unittest.TestResult()) - + If DocFileSuite is used from an interactive session, then files are resolved relative to the directory of sys.argv[0]: @@ -1938,7 +1938,7 @@ >>> suite = doctest.DocFileSuite(test_file, module_relative=False) >>> suite.run(unittest.TestResult()) - + It is an error to specify `package` when `module_relative=False`: @@ -1954,7 +1954,7 @@ ... 'test_doctest4.txt', ... globs={'favorite_color': 'blue'}) >>> suite.run(unittest.TestResult()) - + In this case, we supplied a missing favorite color. You can provide doctest options: @@ -1965,7 +1965,7 @@ ... optionflags=doctest.DONT_ACCEPT_BLANKLINE, ... globs={'favorite_color': 'blue'}) >>> suite.run(unittest.TestResult()) - + And, you can provide setUp and tearDown functions: @@ -1986,7 +1986,7 @@ ... 'test_doctest4.txt', ... setUp=setUp, tearDown=tearDown) >>> suite.run(unittest.TestResult()) - + But the tearDown restores sanity: @@ -2005,7 +2005,7 @@ >>> suite = doctest.DocFileSuite('test_doctest.txt', setUp=setUp) >>> suite.run(unittest.TestResult()) - + Here, we didn't need to use a tearDown function because we modified the test globals. The test globals are @@ -2017,7 +2017,7 @@ >>> suite = doctest.DocFileSuite('test_doctest3.txt') >>> suite.run(unittest.TestResult()) - + If the tests contain non-ASCII characters, we have to specify which encoding the file is encoded with. We do so by using the `encoding` @@ -2028,7 +2028,7 @@ ... 'test_doctest4.txt', ... encoding='utf-8') >>> suite.run(unittest.TestResult()) - + """ Index: Lib/test/test_unittest.py =================================================================== --- Lib/test/test_unittest.py (revision 59056) +++ Lib/test/test_unittest.py (arbetskopia) @@ -1850,9 +1850,52 @@ self.failUnless(result.wasSuccessful()) self.assertEqual(len(result.errors), 0) self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.notImplemented), 0) self.assertEqual(result.testsRun, 1) self.assertEqual(result.shouldStop, False) + # "addNotImplemented(test)" + # ... + # "Called when the test case test raises NotImplementedError" + # ... + # "wasSuccessful() - Returns True if all tests run so far have passed, + # otherwise returns False. A test that raises NotImplementedError is + # considered to have passed." + # ... + # "testsRun - The total number of tests run so far." + # ... + # "errors - A list containing 2-tuples of TestCase instances and + # formatted tracebacks. Each tuple represents a test which raised an + # unexpected exception. Contains formatted + # tracebacks instead of sys.exc_info() results." + # ... + # "failures - A list containing 2-tuples of TestCase instances and + # formatted tracebacks. Each tuple represents a test where a failure was + # explicitly signalled using the TestCase.fail*() or TestCase.assert*() + # methods. Contains formatted tracebacks instead + # of sys.exc_info() results." + # "notImplemented - A list containing the test cases that raised + # NotImplementedError" + def test_addNotImplemented(self): + class Foo(unittest.TestCase): + def test_1(self): + raise NotImplementedError + + test = Foo('test_1') + + result = unittest.TestResult() + + result.startTest(test) + result.addSuccess(test) + result.stopTest(test) + + self.failUnless(result.wasSuccessful()) + self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.notImplemented), 0) + self.assertEqual(result.testsRun, 1) + self.assertEqual(result.shouldStop, False) + # "addFailure(test, err)" # ... # "Called when the test case test signals a failure. err is a tuple of @@ -1894,6 +1937,7 @@ self.failIf(result.wasSuccessful()) self.assertEqual(len(result.errors), 0) + self.assertEqual(len(result.notImplemented), 0) self.assertEqual(len(result.failures), 1) self.assertEqual(result.testsRun, 1) self.assertEqual(result.shouldStop, False) @@ -1945,6 +1989,7 @@ self.failIf(result.wasSuccessful()) self.assertEqual(len(result.errors), 1) self.assertEqual(len(result.failures), 0) + self.assertEqual(len(result.notImplemented), 0) self.assertEqual(result.testsRun, 1) self.assertEqual(result.shouldStop, False)