diff options
author | Michael Foord <fuzzyman@voidspace.org.uk> | 2010-02-10 14:25:12 (GMT) |
---|---|---|
committer | Michael Foord <fuzzyman@voidspace.org.uk> | 2010-02-10 14:25:12 (GMT) |
commit | db43b5a1f5a4b580b68fa0eef0d03d604d7f8805 (patch) | |
tree | 4e8a140ec095adedd3e61d3087f934f3f682f739 | |
parent | 42fb6ab49128f14cd353ec86a177ddda6763812e (diff) | |
download | cpython-db43b5a1f5a4b580b68fa0eef0d03d604d7f8805.zip cpython-db43b5a1f5a4b580b68fa0eef0d03d604d7f8805.tar.gz cpython-db43b5a1f5a4b580b68fa0eef0d03d604d7f8805.tar.bz2 |
Issue 7893 and Issue 7588
-rw-r--r-- | Doc/library/unittest.rst | 28 | ||||
-rw-r--r-- | Lib/test/test_unittest.py | 55 | ||||
-rw-r--r-- | Lib/unittest/__init__.py | 5 | ||||
-rw-r--r-- | Lib/unittest/case.py | 17 | ||||
-rw-r--r-- | Lib/unittest/result.py | 5 | ||||
-rw-r--r-- | Lib/unittest/runner.py | 32 | ||||
-rw-r--r-- | Misc/NEWS | 5 |
7 files changed, 101 insertions, 46 deletions
diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 0c22fd7..67e711c 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -134,7 +134,7 @@ individual tests are defined with methods whose names start with the letters represent tests. The crux of each test is a call to :meth:`~TestCase.assertEqual` to check for an -expected result; :meth:`~TestCase.assert_` to verify a condition; or +expected result; :meth:`~TestCase.assertTrue` to verify a condition; or :meth:`~TestCase.assertRaises` to verify that an expected exception gets raised. These methods are used instead of the :keyword:`assert` statement so the test runner can accumulate all test results and produce a report. @@ -682,6 +682,7 @@ Test cases .. deprecated:: 2.7 :meth:`failUnless`; use one of the ``assert`` variants. + :meth:`assert_`; use :meth:`assertTrue`. .. method:: assertEqual(first, second[, msg]) @@ -1063,13 +1064,7 @@ Test cases Returns a description of the test, or :const:`None` if no description has been provided. The default implementation of this method returns the first line of the test method's docstring, if available, - along with the method name. - - .. versionchanged:: 2.7 - In earlier versions this only returned the first line of the test - method's docstring, if available or the :const:`None`. That led to - undesirable behavior of not printing the test name when someone was - thoughtful enough to write a docstring. + or :const:`None`. .. method:: addTypeEqualityFunc(typeobj, function) @@ -1521,6 +1516,14 @@ Loading and running tests The default implementation appends the test to the instance's :attr:`unexpectedSuccesses` attribute. +.. class:: TextTestResult(stream, descriptions, verbosity) + + A concrete implementation of :class:`TestResult` used by the + :class:`TextTestRunner`. + + .. versionadded:: 2.7 + This class was previously named ``_TextTestResult``. The old name still + exists as an alias but is deprecated. .. data:: defaultTestLoader @@ -1529,7 +1532,7 @@ Loading and running tests instead of repeatedly creating new instances. -.. class:: TextTestRunner([stream[, descriptions[, verbosity]]]) +.. class:: TextTestRunner([stream[, descriptions[, verbosity], [resultclass]]]) A basic test runner implementation which prints results on standard error. It has a few configurable parameters, but is essentially very simple. Graphical @@ -1541,6 +1544,13 @@ Loading and running tests It is not intended to be called directly, but can be overridden in subclasses to provide a custom ``TestResult``. + ``_makeResult()`` instantiates the class or callable passed in the + ``TextTestRunner`` constructor as the ``resultclass`` argument. It + defaults to :class::`TextTestResult` if no ``resultclass`` is provided. + The result class is instantiated with the following arguments:: + + stream, descriptions, verbosity + .. function:: main([module[, defaultTest[, argv[, testRunner[, testLoader[, exit, [verbosity]]]]]]]) diff --git a/Lib/test/test_unittest.py b/Lib/test/test_unittest.py index 0f39085..afe336c 100644 --- a/Lib/test/test_unittest.py +++ b/Lib/test/test_unittest.py @@ -2032,6 +2032,35 @@ class Test_TestResult(TestCase): self.assertTrue(test_case is test) self.assertIsInstance(formatted_exc, str) + def testGetDescriptionWithoutDocstring(self): + result = unittest.TextTestResult(None, True, None) + self.assertEqual( + result.getDescription(self), + 'testGetDescriptionWithoutDocstring (' + __name__ + + '.Test_TestResult)') + + def testGetDescriptionWithOneLineDocstring(self): + """Tests getDescription() for a method with a docstring.""" + result = unittest.TextTestResult(None, True, None) + self.assertEqual( + result.getDescription(self), + ('testGetDescriptionWithOneLineDocstring ' + '(' + __name__ + '.Test_TestResult)\n' + 'Tests getDescription() for a method with a docstring.')) + + def testGetDescriptionWithMultiLineDocstring(self): + """Tests getDescription() for a method with a longer docstring. + The second line of the docstring. + """ + result = unittest.TextTestResult(None, True, None) + self.assertEqual( + result.getDescription(self), + ('testGetDescriptionWithMultiLineDocstring ' + '(' + __name__ + '.Test_TestResult)\n' + 'Tests getDescription() for a method with a longer ' + 'docstring.')) + + ### Support code for Test_TestCase ################################################################ @@ -2445,18 +2474,13 @@ class Test_TestCase(TestCase, TestEquality, TestHashing): self.assertEqual(events, expected) def testShortDescriptionWithoutDocstring(self): - self.assertEqual( - self.shortDescription(), - 'testShortDescriptionWithoutDocstring (' + __name__ + - '.Test_TestCase)') + self.assertIsNone(self.shortDescription()) def testShortDescriptionWithOneLineDocstring(self): """Tests shortDescription() for a method with a docstring.""" self.assertEqual( self.shortDescription(), - ('testShortDescriptionWithOneLineDocstring ' - '(' + __name__ + '.Test_TestCase)\n' - 'Tests shortDescription() for a method with a docstring.')) + 'Tests shortDescription() for a method with a docstring.') def testShortDescriptionWithMultiLineDocstring(self): """Tests shortDescription() for a method with a longer docstring. @@ -2467,10 +2491,8 @@ class Test_TestCase(TestCase, TestEquality, TestHashing): """ self.assertEqual( self.shortDescription(), - ('testShortDescriptionWithMultiLineDocstring ' - '(' + __name__ + '.Test_TestCase)\n' 'Tests shortDescription() for a method with a longer ' - 'docstring.')) + 'docstring.') def testAddTypeEqualityFunc(self): class SadSnake(object): @@ -3491,6 +3513,19 @@ class Test_TextTestRunner(TestCase): # StringIO objects never compare equal, a cheap test instead. self.assertEqual(obj.stream.getvalue(), stream.getvalue()) + def test_resultclass(self): + def MockResultClass(*args): + return args + STREAM = object() + DESCRIPTIONS = object() + VERBOSITY = object() + runner = unittest.TextTestRunner(STREAM, DESCRIPTIONS, VERBOSITY, + resultclass=MockResultClass) + self.assertEqual(runner.resultclass, MockResultClass) + + expectedresult = (runner.stream, DESCRIPTIONS, VERBOSITY) + self.assertEqual(runner._makeResult(), expectedresult) + class TestDiscovery(TestCase): diff --git a/Lib/unittest/__init__.py b/Lib/unittest/__init__.py index e0c64d4..c12e669 100644 --- a/Lib/unittest/__init__.py +++ b/Lib/unittest/__init__.py @@ -60,4 +60,7 @@ from .suite import TestSuite from .loader import (TestLoader, defaultTestLoader, makeSuite, getTestCaseNames, findTestCases) from .main import TestProgram, main -from .runner import TextTestRunner +from .runner import TextTestRunner, TextTestResult + +# deprecated +_TextTestResult = TextTestResult diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index e8ebc90..66f414f 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -216,18 +216,15 @@ class TestCase(object): return result.TestResult() def shortDescription(self): - """Returns both the test method name and first line of its docstring. + """Returns a one-line description of the test, or None if no + description has been provided. - If no docstring is given, only returns the method name. + The default implementation of this method returns the first line of + the specified test method's docstring. """ - desc = str(self) - doc_first_line = None + doc = self._testMethodDoc + return doc and doc.split("\n")[0].strip() or None - if self._testMethodDoc: - doc_first_line = self._testMethodDoc.split("\n")[0].strip() - if doc_first_line: - desc = '\n'.join((desc, doc_first_line)) - return desc def id(self): return "%s.%s" % (util.strclass(self.__class__), self._testMethodName) @@ -488,7 +485,6 @@ class TestCase(object): assertNotEquals = assertNotEqual assertAlmostEquals = assertAlmostEqual assertNotAlmostEquals = assertNotAlmostEqual - assert_ = assertTrue # These fail* assertion method names are pending deprecation and will # be a DeprecationWarning in 3.2; http://bugs.python.org/issue2578 @@ -505,6 +501,7 @@ class TestCase(object): failUnlessAlmostEqual = _deprecate(assertAlmostEqual) failIfAlmostEqual = _deprecate(assertNotAlmostEqual) failUnless = _deprecate(assertTrue) + assert_ = _deprecate(assertTrue) failUnlessRaises = _deprecate(assertRaises) failIf = _deprecate(assertFalse) diff --git a/Lib/unittest/result.py b/Lib/unittest/result.py index 3e32eb0..4538304 100644 --- a/Lib/unittest/result.py +++ b/Lib/unittest/result.py @@ -27,7 +27,7 @@ class TestResult(object): def startTest(self, test): "Called when the given test is about to be run" - self.testsRun = self.testsRun + 1 + self.testsRun += 1 def startTestRun(self): """Called once before any tests are executed. @@ -36,8 +36,7 @@ class TestResult(object): """ def stopTest(self, test): - "Called when the given test has been run" - pass + """Called when the given test has been run""" def stopTestRun(self): """Called once after all tests are executed. diff --git a/Lib/unittest/runner.py b/Lib/unittest/runner.py index 3afbc0e..cbec296 100644 --- a/Lib/unittest/runner.py +++ b/Lib/unittest/runner.py @@ -22,7 +22,7 @@ class _WritelnDecorator(object): self.write('\n') # text-mode streams translate to \r\n if needed -class _TextTestResult(result.TestResult): +class TextTestResult(result.TestResult): """A test result class that can print formatted text results to a stream. Used by TextTestRunner. @@ -31,27 +31,28 @@ class _TextTestResult(result.TestResult): separator2 = '-' * 70 def __init__(self, stream, descriptions, verbosity): - super(_TextTestResult, self).__init__() + super(TextTestResult, self).__init__() self.stream = stream self.showAll = verbosity > 1 self.dots = verbosity == 1 self.descriptions = descriptions def getDescription(self, test): - if self.descriptions: - return test.shortDescription() or str(test) + doc_first_line = test.shortDescription() + if self.descriptions and doc_first_line: + return '\n'.join((str(test), doc_first_line)) else: return str(test) def startTest(self, test): - super(_TextTestResult, self).startTest(test) + super(TextTestResult, self).startTest(test) if self.showAll: self.stream.write(self.getDescription(test)) self.stream.write(" ... ") self.stream.flush() def addSuccess(self, test): - super(_TextTestResult, self).addSuccess(test) + super(TextTestResult, self).addSuccess(test) if self.showAll: self.stream.writeln("ok") elif self.dots: @@ -59,7 +60,7 @@ class _TextTestResult(result.TestResult): self.stream.flush() def addError(self, test, err): - super(_TextTestResult, self).addError(test, err) + super(TextTestResult, self).addError(test, err) if self.showAll: self.stream.writeln("ERROR") elif self.dots: @@ -67,7 +68,7 @@ class _TextTestResult(result.TestResult): self.stream.flush() def addFailure(self, test, err): - super(_TextTestResult, self).addFailure(test, err) + super(TextTestResult, self).addFailure(test, err) if self.showAll: self.stream.writeln("FAIL") elif self.dots: @@ -75,7 +76,7 @@ class _TextTestResult(result.TestResult): self.stream.flush() def addSkip(self, test, reason): - super(_TextTestResult, self).addSkip(test, reason) + super(TextTestResult, self).addSkip(test, reason) if self.showAll: self.stream.writeln("skipped {0!r}".format(reason)) elif self.dots: @@ -83,7 +84,7 @@ class _TextTestResult(result.TestResult): self.stream.flush() def addExpectedFailure(self, test, err): - super(_TextTestResult, self).addExpectedFailure(test, err) + super(TextTestResult, self).addExpectedFailure(test, err) if self.showAll: self.stream.writeln("expected failure") elif self.dots: @@ -91,7 +92,7 @@ class _TextTestResult(result.TestResult): self.stream.flush() def addUnexpectedSuccess(self, test): - super(_TextTestResult, self).addUnexpectedSuccess(test) + super(TextTestResult, self).addUnexpectedSuccess(test) if self.showAll: self.stream.writeln("unexpected success") elif self.dots: @@ -118,13 +119,18 @@ class TextTestRunner(object): It prints out the names of tests as they are run, errors as they occur, and a summary of the results at the end of the test run. """ - def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1): + resultclass = TextTestResult + + def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1, + resultclass=None): self.stream = _WritelnDecorator(stream) self.descriptions = descriptions self.verbosity = verbosity + if resultclass is not None: + self.resultclass = resultclass def _makeResult(self): - return _TextTestResult(self.stream, self.descriptions, self.verbosity) + return self.resultclass(self.stream, self.descriptions, self.verbosity) def run(self, test): "Run the given test case or test suite." @@ -17,6 +17,11 @@ Library - Issue #6003: add an argument to ``zipfile.Zipfile.writestr`` to specify the compression type. +- Issue #7893: ``unittest.TextTestResult`` is made public and a ``resultclass`` + argument added to the TextTestRunner constructor allowing a different result + class to be used without having to subclass. +- Issue 7588: ``unittest.TextTestResult.getDescription`` now includes the test + name in failure reports even if the test has a docstring. What's New in Python 2.7 alpha 3? |