Commit 34c9462d authored by Michael Foord's avatar Michael Foord

Merged revisions 78130 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/trunk

........
  r78130 | michael.foord | 2010-02-10 14:25:12 +0000 (Wed, 10 Feb 2010) | 1 line

  Issue 7893 and Issue 7588
........
parent 99f69ee7
...@@ -195,7 +195,7 @@ individual tests are defined with methods whose names start with the letters ...@@ -195,7 +195,7 @@ individual tests are defined with methods whose names start with the letters
represent tests. represent tests.
The crux of each test is a call to :meth:`~TestCase.assertEqual` to check for an 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. :meth:`~TestCase.assertRaises` to verify that an expected exception gets raised.
These methods are used instead of the :keyword:`assert` statement so the test These methods are used instead of the :keyword:`assert` statement so the test
runner can accumulate all test results and produce a report. runner can accumulate all test results and produce a report.
...@@ -677,6 +677,7 @@ Test cases ...@@ -677,6 +677,7 @@ Test cases
.. deprecated:: 3.1 .. deprecated:: 3.1
:meth:`failUnless`. :meth:`failUnless`.
:meth:`assert_`; use :meth:`assertTrue`.
.. method:: assertEqual(first, second, msg=None) .. method:: assertEqual(first, second, msg=None)
...@@ -1067,14 +1068,13 @@ Test cases ...@@ -1067,14 +1068,13 @@ Test cases
Returns a description of the test, or :const:`None` if no description Returns a description of the test, or :const:`None` if no description
has been provided. The default implementation of this method has been provided. The default implementation of this method
returns the first line of the test method's docstring, if available, returns the first line of the test method's docstring, if available,
along with the method name. or :const:`None`.
.. versionchanged:: 3.1
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.
.. versionchanged:: 3.1,3.2
In 3.1 this was changed to add the test name to the short description
even in the presence of a docstring. This caused compatibility issues
with unittest extensions and adding the test name was moved to the
:class:`TextTestResult`.
.. method:: addTypeEqualityFunc(typeobj, function) .. method:: addTypeEqualityFunc(typeobj, function)
...@@ -1517,6 +1517,14 @@ Loading and running tests ...@@ -1517,6 +1517,14 @@ Loading and running tests
The default implementation appends the test to the instance's The default implementation appends the test to the instance's
:attr:`unexpectedSuccesses` attribute. :attr:`unexpectedSuccesses` attribute.
.. class:: TextTestResult(stream, descriptions, verbosity)
A concrete implementation of :class:`TestResult` used by the
:class:`TextTestRunner`.
.. versionadded:: 3.2
This class was previously named ``_TextTestResult``. The old name still
exists as an alias but is deprecated.
.. data:: defaultTestLoader .. data:: defaultTestLoader
...@@ -1525,7 +1533,7 @@ Loading and running tests ...@@ -1525,7 +1533,7 @@ Loading and running tests
instead of repeatedly creating new instances. instead of repeatedly creating new instances.
.. class:: TextTestRunner(stream=sys.stderr, descriptions=True, verbosity=1) .. class:: TextTestRunner(stream=sys.stderr, descriptions=True, verbosity=1, runnerclass=None)
A basic test runner implementation which prints results on standard error. It A basic test runner implementation which prints results on standard error. It
has a few configurable parameters, but is essentially very simple. Graphical has a few configurable parameters, but is essentially very simple. Graphical
...@@ -1537,6 +1545,12 @@ Loading and running tests ...@@ -1537,6 +1545,12 @@ Loading and running tests
It is not intended to be called directly, but can be overridden in It is not intended to be called directly, but can be overridden in
subclasses to provide a custom ``TestResult``. 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='__main__', defaultTest=None, argv=None, testRunner=None, testLoader=unittest.loader.defaultTestLoader, exit=True, verbosity=1) .. function:: main(module='__main__', defaultTest=None, argv=None, testRunner=None, testLoader=unittest.loader.defaultTestLoader, exit=True, verbosity=1)
......
...@@ -2044,6 +2044,35 @@ class Test_TestResult(TestCase): ...@@ -2044,6 +2044,35 @@ class Test_TestResult(TestCase):
self.assertTrue(test_case is test) self.assertTrue(test_case is test)
self.assertIsInstance(formatted_exc, str) 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 ### Support code for Test_TestCase
################################################################ ################################################################
...@@ -2458,18 +2487,13 @@ class Test_TestCase(TestCase, TestEquality, TestHashing): ...@@ -2458,18 +2487,13 @@ class Test_TestCase(TestCase, TestEquality, TestHashing):
self.assertEqual(events, expected) self.assertEqual(events, expected)
def testShortDescriptionWithoutDocstring(self): def testShortDescriptionWithoutDocstring(self):
self.assertEqual( self.assertIsNone(self.shortDescription())
self.shortDescription(),
'testShortDescriptionWithoutDocstring (' + __name__ +
'.Test_TestCase)')
def testShortDescriptionWithOneLineDocstring(self): def testShortDescriptionWithOneLineDocstring(self):
"""Tests shortDescription() for a method with a docstring.""" """Tests shortDescription() for a method with a docstring."""
self.assertEqual( self.assertEqual(
self.shortDescription(), self.shortDescription(),
('testShortDescriptionWithOneLineDocstring ' 'Tests shortDescription() for a method with a docstring.')
'(' + __name__ + '.Test_TestCase)\n'
'Tests shortDescription() for a method with a docstring.'))
def testShortDescriptionWithMultiLineDocstring(self): def testShortDescriptionWithMultiLineDocstring(self):
"""Tests shortDescription() for a method with a longer docstring. """Tests shortDescription() for a method with a longer docstring.
...@@ -2480,10 +2504,8 @@ class Test_TestCase(TestCase, TestEquality, TestHashing): ...@@ -2480,10 +2504,8 @@ class Test_TestCase(TestCase, TestEquality, TestHashing):
""" """
self.assertEqual( self.assertEqual(
self.shortDescription(), self.shortDescription(),
('testShortDescriptionWithMultiLineDocstring '
'(' + __name__ + '.Test_TestCase)\n'
'Tests shortDescription() for a method with a longer ' 'Tests shortDescription() for a method with a longer '
'docstring.')) 'docstring.')
def testAddTypeEqualityFunc(self): def testAddTypeEqualityFunc(self):
class SadSnake(object): class SadSnake(object):
...@@ -3472,6 +3494,19 @@ class Test_TextTestRunner(TestCase): ...@@ -3472,6 +3494,19 @@ class Test_TextTestRunner(TestCase):
# StringIO objects never compare equal, a cheap test instead. # StringIO objects never compare equal, a cheap test instead.
self.assertEqual(obj.stream.getvalue(), stream.getvalue()) 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): class TestDiscovery(TestCase):
......
...@@ -60,4 +60,7 @@ from .suite import TestSuite ...@@ -60,4 +60,7 @@ from .suite import TestSuite
from .loader import (TestLoader, defaultTestLoader, makeSuite, getTestCaseNames, from .loader import (TestLoader, defaultTestLoader, makeSuite, getTestCaseNames,
findTestCases) findTestCases)
from .main import TestProgram, main from .main import TestProgram, main
from .runner import TextTestRunner from .runner import TextTestRunner, TextTestResult
# deprecated
_TextTestResult = TextTestResult
...@@ -229,18 +229,15 @@ class TestCase(object): ...@@ -229,18 +229,15 @@ class TestCase(object):
return result.TestResult() return result.TestResult()
def shortDescription(self): 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 = self._testMethodDoc
doc_first_line = None 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): def id(self):
return "%s.%s" % (util.strclass(self.__class__), self._testMethodName) return "%s.%s" % (util.strclass(self.__class__), self._testMethodName)
...@@ -501,7 +498,6 @@ class TestCase(object): ...@@ -501,7 +498,6 @@ class TestCase(object):
assertNotEquals = assertNotEqual assertNotEquals = assertNotEqual
assertAlmostEquals = assertAlmostEqual assertAlmostEquals = assertAlmostEqual
assertNotAlmostEquals = assertNotAlmostEqual assertNotAlmostEquals = assertNotAlmostEqual
assert_ = assertTrue
# These fail* assertion method names are pending deprecation and will # These fail* assertion method names are pending deprecation and will
# be a DeprecationWarning in 3.2; http://bugs.python.org/issue2578 # be a DeprecationWarning in 3.2; http://bugs.python.org/issue2578
...@@ -518,6 +514,7 @@ class TestCase(object): ...@@ -518,6 +514,7 @@ class TestCase(object):
failUnlessAlmostEqual = _deprecate(assertAlmostEqual) failUnlessAlmostEqual = _deprecate(assertAlmostEqual)
failIfAlmostEqual = _deprecate(assertNotAlmostEqual) failIfAlmostEqual = _deprecate(assertNotAlmostEqual)
failUnless = _deprecate(assertTrue) failUnless = _deprecate(assertTrue)
assert_ = _deprecate(assertTrue)
failUnlessRaises = _deprecate(assertRaises) failUnlessRaises = _deprecate(assertRaises)
failIf = _deprecate(assertFalse) failIf = _deprecate(assertFalse)
......
...@@ -27,7 +27,7 @@ class TestResult(object): ...@@ -27,7 +27,7 @@ class TestResult(object):
def startTest(self, test): def startTest(self, test):
"Called when the given test is about to be run" "Called when the given test is about to be run"
self.testsRun = self.testsRun + 1 self.testsRun += 1
def startTestRun(self): def startTestRun(self):
"""Called once before any tests are executed. """Called once before any tests are executed.
...@@ -36,8 +36,7 @@ class TestResult(object): ...@@ -36,8 +36,7 @@ class TestResult(object):
""" """
def stopTest(self, test): def stopTest(self, test):
"Called when the given test has been run" """Called when the given test has been run"""
pass
def stopTestRun(self): def stopTestRun(self):
"""Called once after all tests are executed. """Called once after all tests are executed.
......
...@@ -22,7 +22,7 @@ class _WritelnDecorator(object): ...@@ -22,7 +22,7 @@ class _WritelnDecorator(object):
self.write('\n') # text-mode streams translate to \r\n if needed 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. """A test result class that can print formatted text results to a stream.
Used by TextTestRunner. Used by TextTestRunner.
...@@ -31,27 +31,28 @@ class _TextTestResult(result.TestResult): ...@@ -31,27 +31,28 @@ class _TextTestResult(result.TestResult):
separator2 = '-' * 70 separator2 = '-' * 70
def __init__(self, stream, descriptions, verbosity): def __init__(self, stream, descriptions, verbosity):
super(_TextTestResult, self).__init__() super(TextTestResult, self).__init__()
self.stream = stream self.stream = stream
self.showAll = verbosity > 1 self.showAll = verbosity > 1
self.dots = verbosity == 1 self.dots = verbosity == 1
self.descriptions = descriptions self.descriptions = descriptions
def getDescription(self, test): def getDescription(self, test):
if self.descriptions: doc_first_line = test.shortDescription()
return test.shortDescription() or str(test) if self.descriptions and doc_first_line:
return '\n'.join((str(test), doc_first_line))
else: else:
return str(test) return str(test)
def startTest(self, test): def startTest(self, test):
super(_TextTestResult, self).startTest(test) super(TextTestResult, self).startTest(test)
if self.showAll: if self.showAll:
self.stream.write(self.getDescription(test)) self.stream.write(self.getDescription(test))
self.stream.write(" ... ") self.stream.write(" ... ")
self.stream.flush() self.stream.flush()
def addSuccess(self, test): def addSuccess(self, test):
super(_TextTestResult, self).addSuccess(test) super(TextTestResult, self).addSuccess(test)
if self.showAll: if self.showAll:
self.stream.writeln("ok") self.stream.writeln("ok")
elif self.dots: elif self.dots:
...@@ -59,7 +60,7 @@ class _TextTestResult(result.TestResult): ...@@ -59,7 +60,7 @@ class _TextTestResult(result.TestResult):
self.stream.flush() self.stream.flush()
def addError(self, test, err): def addError(self, test, err):
super(_TextTestResult, self).addError(test, err) super(TextTestResult, self).addError(test, err)
if self.showAll: if self.showAll:
self.stream.writeln("ERROR") self.stream.writeln("ERROR")
elif self.dots: elif self.dots:
...@@ -67,7 +68,7 @@ class _TextTestResult(result.TestResult): ...@@ -67,7 +68,7 @@ class _TextTestResult(result.TestResult):
self.stream.flush() self.stream.flush()
def addFailure(self, test, err): def addFailure(self, test, err):
super(_TextTestResult, self).addFailure(test, err) super(TextTestResult, self).addFailure(test, err)
if self.showAll: if self.showAll:
self.stream.writeln("FAIL") self.stream.writeln("FAIL")
elif self.dots: elif self.dots:
...@@ -75,7 +76,7 @@ class _TextTestResult(result.TestResult): ...@@ -75,7 +76,7 @@ class _TextTestResult(result.TestResult):
self.stream.flush() self.stream.flush()
def addSkip(self, test, reason): def addSkip(self, test, reason):
super(_TextTestResult, self).addSkip(test, reason) super(TextTestResult, self).addSkip(test, reason)
if self.showAll: if self.showAll:
self.stream.writeln("skipped {0!r}".format(reason)) self.stream.writeln("skipped {0!r}".format(reason))
elif self.dots: elif self.dots:
...@@ -83,7 +84,7 @@ class _TextTestResult(result.TestResult): ...@@ -83,7 +84,7 @@ class _TextTestResult(result.TestResult):
self.stream.flush() self.stream.flush()
def addExpectedFailure(self, test, err): def addExpectedFailure(self, test, err):
super(_TextTestResult, self).addExpectedFailure(test, err) super(TextTestResult, self).addExpectedFailure(test, err)
if self.showAll: if self.showAll:
self.stream.writeln("expected failure") self.stream.writeln("expected failure")
elif self.dots: elif self.dots:
...@@ -91,7 +92,7 @@ class _TextTestResult(result.TestResult): ...@@ -91,7 +92,7 @@ class _TextTestResult(result.TestResult):
self.stream.flush() self.stream.flush()
def addUnexpectedSuccess(self, test): def addUnexpectedSuccess(self, test):
super(_TextTestResult, self).addUnexpectedSuccess(test) super(TextTestResult, self).addUnexpectedSuccess(test)
if self.showAll: if self.showAll:
self.stream.writeln("unexpected success") self.stream.writeln("unexpected success")
elif self.dots: elif self.dots:
...@@ -118,13 +119,18 @@ class TextTestRunner(object): ...@@ -118,13 +119,18 @@ class TextTestRunner(object):
It prints out the names of tests as they are run, errors as they 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. 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.stream = _WritelnDecorator(stream)
self.descriptions = descriptions self.descriptions = descriptions
self.verbosity = verbosity self.verbosity = verbosity
if resultclass is not None:
self.resultclass = resultclass
def _makeResult(self): def _makeResult(self):
return _TextTestResult(self.stream, self.descriptions, self.verbosity) return self.resultclass(self.stream, self.descriptions, self.verbosity)
def run(self, test): def run(self, test):
"Run the given test case or test suite." "Run the given test case or test suite."
......
...@@ -447,6 +447,13 @@ Library ...@@ -447,6 +447,13 @@ Library
unpickled. This fixes crashes under Windows when trying to run unpickled. This fixes crashes under Windows when trying to run
test_multiprocessing in verbose mode. test_multiprocessing in verbose mode.
- 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.
- Issue #3001: Add a C implementation of recursive locks which is used by - Issue #3001: Add a C implementation of recursive locks which is used by
default when instantiating a `threading.RLock` object. This makes default when instantiating a `threading.RLock` object. This makes
recursive locks as fast as regular non-recursive locks (previously, recursive locks as fast as regular non-recursive locks (previously,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment