mirror of
https://github.com/python/cpython.git
synced 2025-12-08 18:32:16 +00:00
- setUp and tearDown functions are now passed the test object
- Added a set_unittest_reportflags to set default reporting flags used when running doctests under unittest control.
This commit is contained in:
parent
91879ab8ea
commit
f54bad4564
2 changed files with 235 additions and 22 deletions
141
Lib/doctest.py
141
Lib/doctest.py
|
|
@ -179,6 +179,7 @@ __all__ = [
|
||||||
'REPORT_UDIFF',
|
'REPORT_UDIFF',
|
||||||
'REPORT_CDIFF',
|
'REPORT_CDIFF',
|
||||||
'REPORT_NDIFF',
|
'REPORT_NDIFF',
|
||||||
|
'REPORT_ONLY_FIRST_FAILURE',
|
||||||
# 1. Utility Functions
|
# 1. Utility Functions
|
||||||
'is_private',
|
'is_private',
|
||||||
# 2. Example & DocTest
|
# 2. Example & DocTest
|
||||||
|
|
@ -1991,6 +1992,65 @@ class Tester:
|
||||||
## 8. Unittest Support
|
## 8. Unittest Support
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
|
_unittest_reportflags = 0
|
||||||
|
valid_unittest_reportflags = (
|
||||||
|
REPORT_CDIFF |
|
||||||
|
REPORT_UDIFF |
|
||||||
|
REPORT_NDIFF |
|
||||||
|
REPORT_ONLY_FIRST_FAILURE
|
||||||
|
)
|
||||||
|
def set_unittest_reportflags(flags):
|
||||||
|
"""Sets the unit test option flags
|
||||||
|
|
||||||
|
The old flag is returned so that a runner could restore the old
|
||||||
|
value if it wished to:
|
||||||
|
|
||||||
|
>>> old = _unittest_reportflags
|
||||||
|
>>> set_unittest_reportflags(REPORT_NDIFF |
|
||||||
|
... REPORT_ONLY_FIRST_FAILURE) == old
|
||||||
|
True
|
||||||
|
|
||||||
|
>>> import doctest
|
||||||
|
>>> doctest._unittest_reportflags == (REPORT_NDIFF |
|
||||||
|
... REPORT_ONLY_FIRST_FAILURE)
|
||||||
|
True
|
||||||
|
|
||||||
|
Only reporting flags can be set:
|
||||||
|
|
||||||
|
>>> set_unittest_reportflags(ELLIPSIS)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValueError: ('Invalid flags passed', 8)
|
||||||
|
|
||||||
|
>>> set_unittest_reportflags(old) == (REPORT_NDIFF |
|
||||||
|
... REPORT_ONLY_FIRST_FAILURE)
|
||||||
|
True
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# extract the valid reporting flags:
|
||||||
|
rflags = flags & valid_unittest_reportflags
|
||||||
|
|
||||||
|
# Now remove these flags from the given flags
|
||||||
|
nrflags = flags ^ rflags
|
||||||
|
|
||||||
|
if nrflags:
|
||||||
|
raise ValueError("Invalid flags passed", flags)
|
||||||
|
|
||||||
|
global _unittest_reportflags
|
||||||
|
old = _unittest_reportflags
|
||||||
|
_unittest_reportflags = flags
|
||||||
|
return old
|
||||||
|
|
||||||
|
|
||||||
|
class FakeModule:
|
||||||
|
"""Fake module created by tests
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, dict, name):
|
||||||
|
self.__dict__ = dict
|
||||||
|
self.__name__ = name
|
||||||
|
|
||||||
class DocTestCase(unittest.TestCase):
|
class DocTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
|
def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
|
||||||
|
|
@ -2004,23 +2064,37 @@ class DocTestCase(unittest.TestCase):
|
||||||
self._dt_tearDown = tearDown
|
self._dt_tearDown = tearDown
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
test = self._dt_test
|
||||||
|
|
||||||
if self._dt_setUp is not None:
|
if self._dt_setUp is not None:
|
||||||
self._dt_setUp()
|
self._dt_setUp(test)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
test = self._dt_test
|
||||||
|
|
||||||
if self._dt_tearDown is not None:
|
if self._dt_tearDown is not None:
|
||||||
self._dt_tearDown()
|
self._dt_tearDown(test)
|
||||||
|
|
||||||
|
test.globs.clear()
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
test = self._dt_test
|
test = self._dt_test
|
||||||
old = sys.stdout
|
old = sys.stdout
|
||||||
new = StringIO()
|
new = StringIO()
|
||||||
runner = DocTestRunner(optionflags=self._dt_optionflags,
|
optionflags = self._dt_optionflags
|
||||||
|
|
||||||
|
if not (optionflags & valid_unittest_reportflags):
|
||||||
|
# The option flags don't include any reporting flags,
|
||||||
|
# so add the default reporting flags
|
||||||
|
optionflags |= _unittest_reportflags
|
||||||
|
|
||||||
|
runner = DocTestRunner(optionflags=optionflags,
|
||||||
checker=self._dt_checker, verbose=False)
|
checker=self._dt_checker, verbose=False)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
runner.DIVIDER = "-"*70
|
runner.DIVIDER = "-"*70
|
||||||
failures, tries = runner.run(test, out=new.write)
|
failures, tries = runner.run(
|
||||||
|
test, out=new.write, clear_globs=False)
|
||||||
finally:
|
finally:
|
||||||
sys.stdout = old
|
sys.stdout = old
|
||||||
|
|
||||||
|
|
@ -2105,9 +2179,11 @@ class DocTestCase(unittest.TestCase):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
self.setUp()
|
||||||
runner = DebugRunner(optionflags=self._dt_optionflags,
|
runner = DebugRunner(optionflags=self._dt_optionflags,
|
||||||
checker=self._dt_checker, verbose=False)
|
checker=self._dt_checker, verbose=False)
|
||||||
runner.run(self._dt_test)
|
runner.run(self._dt_test)
|
||||||
|
self.tearDown()
|
||||||
|
|
||||||
def id(self):
|
def id(self):
|
||||||
return self._dt_test.name
|
return self._dt_test.name
|
||||||
|
|
@ -2121,10 +2197,8 @@ class DocTestCase(unittest.TestCase):
|
||||||
def shortDescription(self):
|
def shortDescription(self):
|
||||||
return "Doctest: " + self._dt_test.name
|
return "Doctest: " + self._dt_test.name
|
||||||
|
|
||||||
def DocTestSuite(module=None, globs=None, extraglobs=None,
|
def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None,
|
||||||
optionflags=0, test_finder=None,
|
**options):
|
||||||
setUp=lambda: None, tearDown=lambda: None,
|
|
||||||
checker=None):
|
|
||||||
"""
|
"""
|
||||||
Convert doctest tests for a module to a unittest test suite.
|
Convert doctest tests for a module to a unittest test suite.
|
||||||
|
|
||||||
|
|
@ -2138,6 +2212,32 @@ def DocTestSuite(module=None, globs=None, extraglobs=None,
|
||||||
can be either a module or a module name.
|
can be either a module or a module name.
|
||||||
|
|
||||||
If no argument is given, the calling module is used.
|
If no argument is given, the calling module is used.
|
||||||
|
|
||||||
|
A number of options may be provided as keyword arguments:
|
||||||
|
|
||||||
|
package
|
||||||
|
The name of a Python package. Text-file paths will be
|
||||||
|
interpreted relative to the directory containing this package.
|
||||||
|
The package may be supplied as a package object or as a dotted
|
||||||
|
package name.
|
||||||
|
|
||||||
|
setUp
|
||||||
|
The name of a set-up function. This is called before running the
|
||||||
|
tests in each file. The setUp function will be passed a DocTest
|
||||||
|
object. The setUp function can access the test globals as the
|
||||||
|
globs attribute of the test passed.
|
||||||
|
|
||||||
|
tearDown
|
||||||
|
The name of a tear-down function. This is called after running the
|
||||||
|
tests in each file. The tearDown function will be passed a DocTest
|
||||||
|
object. The tearDown function can access the test globals as the
|
||||||
|
globs attribute of the test passed.
|
||||||
|
|
||||||
|
globs
|
||||||
|
A dictionary containing initial global variables for the tests.
|
||||||
|
|
||||||
|
optionflags
|
||||||
|
A set of doctest option flags expressed as an integer.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if test_finder is None:
|
if test_finder is None:
|
||||||
|
|
@ -2147,7 +2247,9 @@ def DocTestSuite(module=None, globs=None, extraglobs=None,
|
||||||
tests = test_finder.find(module, globs=globs, extraglobs=extraglobs)
|
tests = test_finder.find(module, globs=globs, extraglobs=extraglobs)
|
||||||
if globs is None:
|
if globs is None:
|
||||||
globs = module.__dict__
|
globs = module.__dict__
|
||||||
if not tests: # [XX] why do we want to do this?
|
if not tests:
|
||||||
|
# Why do we want to do this? Because it reveals a bug that might
|
||||||
|
# otherwise be hidden.
|
||||||
raise ValueError(module, "has no tests")
|
raise ValueError(module, "has no tests")
|
||||||
|
|
||||||
tests.sort()
|
tests.sort()
|
||||||
|
|
@ -2160,8 +2262,7 @@ def DocTestSuite(module=None, globs=None, extraglobs=None,
|
||||||
if filename[-4:] in (".pyc", ".pyo"):
|
if filename[-4:] in (".pyc", ".pyo"):
|
||||||
filename = filename[:-1]
|
filename = filename[:-1]
|
||||||
test.filename = filename
|
test.filename = filename
|
||||||
suite.addTest(DocTestCase(test, optionflags, setUp, tearDown,
|
suite.addTest(DocTestCase(test, **options))
|
||||||
checker))
|
|
||||||
|
|
||||||
return suite
|
return suite
|
||||||
|
|
||||||
|
|
@ -2179,9 +2280,7 @@ class DocFileCase(DocTestCase):
|
||||||
% (self._dt_test.name, self._dt_test.filename, err)
|
% (self._dt_test.name, self._dt_test.filename, err)
|
||||||
)
|
)
|
||||||
|
|
||||||
def DocFileTest(path, package=None, globs=None,
|
def DocFileTest(path, package=None, globs=None, **options):
|
||||||
setUp=None, tearDown=None,
|
|
||||||
optionflags=0):
|
|
||||||
package = _normalize_module(package)
|
package = _normalize_module(package)
|
||||||
name = path.split('/')[-1]
|
name = path.split('/')[-1]
|
||||||
dir = os.path.split(package.__file__)[0]
|
dir = os.path.split(package.__file__)[0]
|
||||||
|
|
@ -2193,7 +2292,7 @@ def DocFileTest(path, package=None, globs=None,
|
||||||
|
|
||||||
test = DocTestParser().get_doctest(doc, globs, name, path, 0)
|
test = DocTestParser().get_doctest(doc, globs, name, path, 0)
|
||||||
|
|
||||||
return DocFileCase(test, optionflags, setUp, tearDown)
|
return DocFileCase(test, **options)
|
||||||
|
|
||||||
def DocFileSuite(*paths, **kw):
|
def DocFileSuite(*paths, **kw):
|
||||||
"""Creates a suite of doctest files.
|
"""Creates a suite of doctest files.
|
||||||
|
|
@ -2213,14 +2312,22 @@ def DocFileSuite(*paths, **kw):
|
||||||
|
|
||||||
setUp
|
setUp
|
||||||
The name of a set-up function. This is called before running the
|
The name of a set-up function. This is called before running the
|
||||||
tests in each file.
|
tests in each file. The setUp function will be passed a DocTest
|
||||||
|
object. The setUp function can access the test globals as the
|
||||||
|
globs attribute of the test passed.
|
||||||
|
|
||||||
tearDown
|
tearDown
|
||||||
The name of a tear-down function. This is called after running the
|
The name of a tear-down function. This is called after running the
|
||||||
tests in each file.
|
tests in each file. The tearDown function will be passed a DocTest
|
||||||
|
object. The tearDown function can access the test globals as the
|
||||||
|
globs attribute of the test passed.
|
||||||
|
|
||||||
globs
|
globs
|
||||||
A dictionary containing initial global variables for the tests.
|
A dictionary containing initial global variables for the tests.
|
||||||
|
|
||||||
|
optionflags
|
||||||
|
A set of doctest option flags expressed as an integer.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
suite = unittest.TestSuite()
|
suite = unittest.TestSuite()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1653,11 +1653,11 @@ def test_DocTestSuite():
|
||||||
|
|
||||||
You can supply setUp and tearDown functions:
|
You can supply setUp and tearDown functions:
|
||||||
|
|
||||||
>>> def setUp():
|
>>> def setUp(t):
|
||||||
... import test.test_doctest
|
... import test.test_doctest
|
||||||
... test.test_doctest.sillySetup = True
|
... test.test_doctest.sillySetup = True
|
||||||
|
|
||||||
>>> def tearDown():
|
>>> def tearDown(t):
|
||||||
... import test.test_doctest
|
... import test.test_doctest
|
||||||
... del test.test_doctest.sillySetup
|
... del test.test_doctest.sillySetup
|
||||||
|
|
||||||
|
|
@ -1676,6 +1676,21 @@ def test_DocTestSuite():
|
||||||
...
|
...
|
||||||
AttributeError: 'module' object has no attribute 'sillySetup'
|
AttributeError: 'module' object has no attribute 'sillySetup'
|
||||||
|
|
||||||
|
The setUp and tearDown funtions are passed test objects. Here
|
||||||
|
we'll use the setUp function to supply the missing variable y:
|
||||||
|
|
||||||
|
>>> def setUp(test):
|
||||||
|
... test.globs['y'] = 1
|
||||||
|
|
||||||
|
>>> suite = doctest.DocTestSuite('test.sample_doctest', setUp=setUp)
|
||||||
|
>>> suite.run(unittest.TestResult())
|
||||||
|
<unittest.TestResult run=9 errors=0 failures=3>
|
||||||
|
|
||||||
|
Here, we didn't need to use a tearDown function because we
|
||||||
|
modified the test globals, which are a copy of the
|
||||||
|
sample_doctest module dictionary. The test globals are
|
||||||
|
automatically cleared for us after a test.
|
||||||
|
|
||||||
Finally, you can provide an alternate test finder. Here we'll
|
Finally, you can provide an alternate test finder. Here we'll
|
||||||
use a custom test_finder to to run just the test named bar.
|
use a custom test_finder to to run just the test named bar.
|
||||||
However, the test in the module docstring, and the two tests
|
However, the test in the module docstring, and the two tests
|
||||||
|
|
@ -1744,11 +1759,11 @@ def test_DocFileSuite():
|
||||||
|
|
||||||
You can supply setUp and teatDoen functions:
|
You can supply setUp and teatDoen functions:
|
||||||
|
|
||||||
>>> def setUp():
|
>>> def setUp(t):
|
||||||
... import test.test_doctest
|
... import test.test_doctest
|
||||||
... test.test_doctest.sillySetup = True
|
... test.test_doctest.sillySetup = True
|
||||||
|
|
||||||
>>> def tearDown():
|
>>> def tearDown(t):
|
||||||
... import test.test_doctest
|
... import test.test_doctest
|
||||||
... del test.test_doctest.sillySetup
|
... del test.test_doctest.sillySetup
|
||||||
|
|
||||||
|
|
@ -1768,6 +1783,21 @@ def test_DocFileSuite():
|
||||||
...
|
...
|
||||||
AttributeError: 'module' object has no attribute 'sillySetup'
|
AttributeError: 'module' object has no attribute 'sillySetup'
|
||||||
|
|
||||||
|
The setUp and tearDown funtions are passed test objects.
|
||||||
|
Here, we'll use a setUp function to set the favorite color in
|
||||||
|
test_doctest.txt:
|
||||||
|
|
||||||
|
>>> def setUp(test):
|
||||||
|
... test.globs['favorite_color'] = 'blue'
|
||||||
|
|
||||||
|
>>> suite = doctest.DocFileSuite('test_doctest.txt', setUp=setUp)
|
||||||
|
>>> suite.run(unittest.TestResult())
|
||||||
|
<unittest.TestResult run=1 errors=0 failures=0>
|
||||||
|
|
||||||
|
Here, we didn't need to use a tearDown function because we
|
||||||
|
modified the test globals. The test globals are
|
||||||
|
automatically cleared for us after a test.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_trailing_space_in_test():
|
def test_trailing_space_in_test():
|
||||||
|
|
@ -1779,6 +1809,82 @@ def test_trailing_space_in_test():
|
||||||
foo \n
|
foo \n
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def test_unittest_reportflags():
|
||||||
|
"""Default unittest reporting flags can be set to control reporting
|
||||||
|
|
||||||
|
Here, we'll set the REPORT_ONLY_FIRST_FAILURE option so we see
|
||||||
|
only the first failure of each test. First, we'll look at the
|
||||||
|
output without the flag. The file test_doctest.txt file has two
|
||||||
|
tests. They both fail if blank lines are disabled:
|
||||||
|
|
||||||
|
>>> suite = doctest.DocFileSuite('test_doctest.txt',
|
||||||
|
... optionflags=doctest.DONT_ACCEPT_BLANKLINE)
|
||||||
|
>>> import unittest
|
||||||
|
>>> result = suite.run(unittest.TestResult())
|
||||||
|
>>> print result.failures[0][1] # doctest: +ELLIPSIS
|
||||||
|
Traceback ...
|
||||||
|
Failed example:
|
||||||
|
favorite_color
|
||||||
|
...
|
||||||
|
Failed example:
|
||||||
|
if 1:
|
||||||
|
...
|
||||||
|
|
||||||
|
Note that we see both failures displayed.
|
||||||
|
|
||||||
|
>>> old = doctest.set_unittest_reportflags(
|
||||||
|
... doctest.REPORT_ONLY_FIRST_FAILURE)
|
||||||
|
|
||||||
|
Now, when we run the test:
|
||||||
|
|
||||||
|
>>> result = suite.run(unittest.TestResult())
|
||||||
|
>>> print result.failures[0][1] # doctest: +ELLIPSIS
|
||||||
|
Traceback ...
|
||||||
|
Failed example:
|
||||||
|
favorite_color
|
||||||
|
Exception raised:
|
||||||
|
...
|
||||||
|
NameError: name 'favorite_color' is not defined
|
||||||
|
<BLANKLINE>
|
||||||
|
<BLANKLINE>
|
||||||
|
|
||||||
|
We get only the first failure.
|
||||||
|
|
||||||
|
If we give any reporting options when we set up the tests,
|
||||||
|
however:
|
||||||
|
|
||||||
|
>>> suite = doctest.DocFileSuite('test_doctest.txt',
|
||||||
|
... optionflags=doctest.DONT_ACCEPT_BLANKLINE | doctest.REPORT_NDIFF)
|
||||||
|
|
||||||
|
Then the default eporting options are ignored:
|
||||||
|
|
||||||
|
>>> result = suite.run(unittest.TestResult())
|
||||||
|
>>> print result.failures[0][1] # doctest: +ELLIPSIS
|
||||||
|
Traceback ...
|
||||||
|
Failed example:
|
||||||
|
favorite_color
|
||||||
|
...
|
||||||
|
Failed example:
|
||||||
|
if 1:
|
||||||
|
print 'a'
|
||||||
|
print
|
||||||
|
print 'b'
|
||||||
|
Differences (ndiff with -expected +actual):
|
||||||
|
a
|
||||||
|
- <BLANKLINE>
|
||||||
|
+
|
||||||
|
b
|
||||||
|
<BLANKLINE>
|
||||||
|
<BLANKLINE>
|
||||||
|
|
||||||
|
|
||||||
|
Test runners can restore the formatting flags after they run:
|
||||||
|
|
||||||
|
>>> ignored = doctest.set_unittest_reportflags(old)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
# old_test1, ... used to live in doctest.py, but cluttered it. Note
|
# old_test1, ... used to live in doctest.py, but cluttered it. Note
|
||||||
# that these use the deprecated doctest.Tester, so should go away (or
|
# that these use the deprecated doctest.Tester, so should go away (or
|
||||||
# be rewritten) someday.
|
# be rewritten) someday.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue