Properly handling different test cases at the same time.

import unittest, doctest, re, inspect, typing, types, contextlib

Sample test methods.

def test_me(): assert True

class TestClass(unittest.TestCase):
    def test_this(self): assert False

Methods

Combine unittest and doctest suites.

def make_test_suite(*objects: typing.Union[
    unittest.TestCase, types.FunctionType, str
]) -> unittest.TestSuite:

Create a test suite from a few different objects.

    suite, doctest_suite = unittest.TestSuite(), doctest.DocTestSuite()
    suite.addTest(doctest_suite)
    for object in objects:
        if isinstance(object, type) and issubclass(object, unittest.TestCase):

Load unittest.TestCase when that type is encountered.

            suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(object))
        elif isinstance(object, str):

In the case of strings we’ll use the doctest module to find standard doctests and inline markdown code.

            doctest_suite.addTest(doctest.DocTestCase(doctest.DocTestParser().get_doctest(object, globals(), __name__, __name__, 0)))
            doctest_suite.addTest(doctest.DocTestCase(InlineDoctestParser().get_doctest(object, globals(), __name__, __name__, 0), checker=NullOutputCheck))
        elif inspect.isfunction(object): 

When a function is encountered create a test case from that.

            suite.addTest(unittest.FunctionTestCase(object))
    return suite

Run the main test suite

def run(suite: unittest.TestCase) -> unittest.TestResult:

run a test suite likely created from make_test_suite.

    result = unittest.TestResult(); suite.run(result)
    if result.failures:
        sys.stderr.writelines((str(result) + '\n' + '\n'.join(msg for text, msg in result.failures)).splitlines(True))
    return result

class NullOutputCheck(doctest.OutputChecker):

A compatability tool for tests as long as they don’t return an exception.

    def check_output(self, *e): return True

A doctest parser for inline code objects.

class InlineDoctestParser(doctest.DocTestParser):

In "pidgy", we want all code to compute. Specifically we add the ability to run inline markdown code objects. as tests.

    _EXAMPLE_RE = re.compile(r'`(?P<indent>\s{0})'
r'(?P<source>[^`].*?)'
r'`')
    def _parse_example(self, m, name, lineno): return m.group('source'), None, "...", None

Running the examples.

Running some made up tests.

result = run(make_test_suite("""What is the `range` 

>>> 10
10

""", test_me, TestClass))

Expected output.

<unittest.result.TestResult run=4 errors=0 failures=1>
Traceback (most recent call last):
  File "<ipython-input-31-5ae775417371>", line 10, in test_this
    def test_this(self): assert False
AssertionError