Skip to content

Commit 100a57e

Browse files
author
Saurabh B
committed
Add support for custom test case and runner in both DocTestSuite and DocFileSuite
1 parent 5ea9010 commit 100a57e

File tree

3 files changed

+120
-9
lines changed

3 files changed

+120
-9
lines changed

Doc/library/doctest.rst

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,7 @@ There are two main functions for creating :class:`unittest.TestSuite` instances
10381038
from text files and modules with doctests:
10391039

10401040

1041-
.. function:: DocFileSuite(*paths, module_relative=True, package=None, setUp=None, tearDown=None, globs=None, optionflags=0, parser=DocTestParser(), encoding=None)
1041+
.. function:: DocFileSuite(*paths, module_relative=True, package=None, setUp=None, tearDown=None, globs=None, optionflags=0, parser=DocTestParser(), encoding=None, test_case=DocFileCase, runner=DocTestRunner)
10421042

10431043
Convert doctest tests from one or more text files to a
10441044
:class:`unittest.TestSuite`.
@@ -1102,11 +1102,24 @@ from text files and modules with doctests:
11021102
Optional argument *encoding* specifies an encoding that should be used to
11031103
convert the file to unicode.
11041104

1105+
Optional argument *test_case* specifies the :class:`DocFileCase` class (or a
1106+
subclass) that should be used to create test cases. By default, :class:`DocFileCase`
1107+
is used. This allows for custom test case classes that can add additional behavior
1108+
or attributes to the test cases.
1109+
1110+
Optional argument *runner* specifies the :class:`DocTestRunner` class (or a
1111+
subclass) that should be used to run the tests. By default, :class:`DocTestRunner`
1112+
is used.
1113+
11051114
The global ``__file__`` is added to the globals provided to doctests loaded
11061115
from a text file using :func:`DocFileSuite`.
11071116

1117+
.. versionchanged:: 3.13
1118+
Added *test_case* and *runner* parameters to support user specified test case
1119+
and runner in DocFileSuite.
1120+
11081121

1109-
.. function:: DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None, setUp=None, tearDown=None, optionflags=0, checker=None)
1122+
.. function:: DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None, setUp=None, tearDown=None, optionflags=0, checker=None, test_case=DocTestCase, runner=DocTestRunner)
11101123

11111124
Convert doctest tests for a module to a :class:`unittest.TestSuite`.
11121125

@@ -1134,12 +1147,25 @@ from text files and modules with doctests:
11341147
Optional arguments *setUp*, *tearDown*, and *optionflags* are the same as for
11351148
function :func:`DocFileSuite` above.
11361149

1150+
Optional argument *test_case* specifies the :class:`DocTestCase` class (or a
1151+
subclass) that should be used to create test cases. By default, :class:`DocTestCase`
1152+
is used. This allows for custom test case classes that can add additional behavior
1153+
or attributes to the test cases.
1154+
1155+
Optional argument *runner* specifies the :class:`DocTestRunner` class (or a
1156+
subclass) that should be used to run the tests. By default, :class:`DocTestRunner`
1157+
is used.
1158+
11371159
This function uses the same search technique as :func:`testmod`.
11381160

11391161
.. versionchanged:: 3.5
11401162
:func:`DocTestSuite` returns an empty :class:`unittest.TestSuite` if *module*
11411163
contains no docstrings instead of raising :exc:`ValueError`.
11421164

1165+
.. versionchanged:: 3.13
1166+
Added *runner* and *test_case* parameters to support custom test runners
1167+
and test case classes in DocTestSuite.
1168+
11431169
.. exception:: failureException
11441170

11451171
When doctests which have been converted to unit tests by :func:`DocFileSuite`

Lib/doctest.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2275,14 +2275,15 @@ def set_unittest_reportflags(flags):
22752275
class DocTestCase(unittest.TestCase):
22762276

22772277
def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
2278-
checker=None):
2278+
checker=None, runner=DocTestRunner):
22792279

22802280
unittest.TestCase.__init__(self)
22812281
self._dt_optionflags = optionflags
22822282
self._dt_checker = checker
22832283
self._dt_test = test
22842284
self._dt_setUp = setUp
22852285
self._dt_tearDown = tearDown
2286+
self._dt_runner = runner
22862287

22872288
def setUp(self):
22882289
test = self._dt_test
@@ -2312,7 +2313,7 @@ def runTest(self):
23122313
# so add the default reporting flags
23132314
optionflags |= _unittest_reportflags
23142315

2315-
runner = DocTestRunner(optionflags=optionflags,
2316+
runner = self._dt_runner(optionflags=optionflags,
23162317
checker=self._dt_checker, verbose=False)
23172318

23182319
try:
@@ -2460,7 +2461,7 @@ def _removeTestAtIndex(self, index):
24602461

24612462

24622463
def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None,
2463-
**options):
2464+
test_case=DocTestCase, runner=DocTestRunner, **options):
24642465
"""
24652466
Convert doctest tests for a module to a unittest test suite.
24662467
@@ -2494,6 +2495,12 @@ def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None,
24942495
24952496
optionflags
24962497
A set of doctest option flags expressed as an integer.
2498+
2499+
test_case
2500+
A custom optional DocTestCase class to use for test cases.
2501+
2502+
runner
2503+
A custom optional DocTestRunner class to use for running tests.
24972504
"""
24982505

24992506
if test_finder is None:
@@ -2519,7 +2526,7 @@ def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None,
25192526
if filename[-4:] == ".pyc":
25202527
filename = filename[:-1]
25212528
test.filename = filename
2522-
suite.addTest(DocTestCase(test, **options))
2529+
suite.addTest(test_case(test, runner=runner, **options))
25232530

25242531
return suite
25252532

@@ -2538,7 +2545,8 @@ def format_failure(self, err):
25382545

25392546
def DocFileTest(path, module_relative=True, package=None,
25402547
globs=None, parser=DocTestParser(),
2541-
encoding=None, **options):
2548+
encoding=None, test_case=DocFileCase,
2549+
runner=DocTestRunner, **options):
25422550
if globs is None:
25432551
globs = {}
25442552
else:
@@ -2560,7 +2568,7 @@ def DocFileTest(path, module_relative=True, package=None,
25602568

25612569
# Convert it to a test, and wrap it in a DocFileCase.
25622570
test = parser.get_doctest(doc, globs, name, path, 0)
2563-
return DocFileCase(test, **options)
2571+
return test_case(test, runner=runner, **options)
25642572

25652573
def DocFileSuite(*paths, **kw):
25662574
"""A unittest suite for one or more doctest files.
@@ -2617,6 +2625,12 @@ def DocFileSuite(*paths, **kw):
26172625
26182626
encoding
26192627
An encoding that will be used to convert the files to unicode.
2628+
2629+
test_case
2630+
A custom DocFileCase subclass to use for the tests.
2631+
2632+
runner
2633+
A custom DocTestRunner subclass to use for running the tests.
26202634
"""
26212635
suite = _DocTestSuite()
26222636

@@ -2626,8 +2640,17 @@ def DocFileSuite(*paths, **kw):
26262640
if kw.get('module_relative', True):
26272641
kw['package'] = _normalize_module(kw.get('package'))
26282642

2643+
test_case = kw.pop('test_case', DocFileCase)
2644+
runner = kw.pop('runner', DocTestRunner)
2645+
26292646
for path in paths:
2630-
suite.addTest(DocFileTest(path, **kw))
2647+
test_instance = DocFileTest(
2648+
path=path,
2649+
test_case=test_case,
2650+
runner=runner,
2651+
**kw
2652+
)
2653+
suite.addTest(test_instance)
26312654

26322655
return suite
26332656

Lib/test/test_doctest/test_doctest.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2383,6 +2383,36 @@ def test_DocTestSuite():
23832383
modified the test globals, which are a copy of the
23842384
sample_doctest module dictionary. The test globals are
23852385
automatically cleared for us after a test.
2386+
2387+
We can also provide a custom test case class:
2388+
2389+
>>> class CustomDocTestCase(doctest.DocTestCase):
2390+
... def __init__(self, test, **options):
2391+
... super().__init__(test, **options)
2392+
... self.custom_attr = "custom_value"
2393+
... def runTest(self):
2394+
... self.assertEqual(self.custom_attr, "custom_value")
2395+
... super().runTest()
2396+
2397+
>>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest',
2398+
... test_case=CustomDocTestCase)
2399+
>>> result = suite.run(unittest.TestResult())
2400+
>>> result
2401+
<unittest.result.TestResult run=9 errors=0 failures=4>
2402+
2403+
We can also provide both a custom test case class and a custom runner class:
2404+
2405+
>>> class CustomDocTestRunner(doctest.DocTestRunner):
2406+
... def __init__(self, **options):
2407+
... super().__init__(**options)
2408+
... self.custom_attr = "custom_runner"
2409+
>>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest',
2410+
... test_case=CustomDocTestCase,
2411+
... runner=CustomDocTestRunner)
2412+
>>> result = suite.run(unittest.TestResult())
2413+
>>> result
2414+
<unittest.result.TestResult run=9 errors=0 failures=4>
2415+
23862416
"""
23872417

23882418
def test_DocFileSuite():
@@ -2543,6 +2573,38 @@ def test_DocFileSuite():
25432573
>>> suite.run(unittest.TestResult())
25442574
<unittest.result.TestResult run=3 errors=0 failures=2>
25452575
2576+
We can also provide a custom test case class:
2577+
2578+
>>> class CustomDocTestCase(doctest.DocFileCase):
2579+
... def __init__(self, test, **options):
2580+
... super().__init__(test, **options)
2581+
... self.custom_attr = "custom_value"
2582+
... def runTest(self):
2583+
... self.assertEqual(self.custom_attr, "custom_value")
2584+
... super().runTest()
2585+
2586+
>>> suite = doctest.DocFileSuite('test_doctest.txt',
2587+
... globs={'favorite_color': 'blue'},
2588+
... test_case=CustomDocTestCase)
2589+
>>> result = suite.run(unittest.TestResult())
2590+
>>> result
2591+
<unittest.result.TestResult run=1 errors=0 failures=0>
2592+
2593+
We can also provide both a custom test case class and a custom runner class:
2594+
2595+
>>> class CustomDocTestRunner(doctest.DocTestRunner):
2596+
... def __init__(self, **options):
2597+
... super().__init__(**options)
2598+
... self.custom_attr = "custom_runner"
2599+
2600+
>>> suite = doctest.DocFileSuite('test_doctest.txt',
2601+
... globs={'favorite_color': 'blue'},
2602+
... test_case=CustomDocTestCase,
2603+
... runner=CustomDocTestRunner)
2604+
>>> result = suite.run(unittest.TestResult())
2605+
>>> result
2606+
<unittest.result.TestResult run=1 errors=0 failures=0>
2607+
25462608
"""
25472609

25482610
def test_trailing_space_in_test():

0 commit comments

Comments
 (0)