Skip to content

Instantly share code, notes, and snippets.

@kernc
Last active February 6, 2016 14:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kernc/0dccae6542465d59a331 to your computer and use it in GitHub Desktop.
Save kernc/0dccae6542465d59a331 to your computer and use it in GitHub Desktop.
unittest.TestSuite module that recursively loads all doctests from my package
"""
unittest.TestSuite module that recursively loads all doctests from my package
"""
from doctest import DocTestSuite, ELLIPSIS, NORMALIZE_WHITESPACE
SKIP_DIRS = (
'MyPackage/unimportable-modules/',
'MyPackage/skip_this_too',
)
def find_modules(package):
"""Return a recursive list of submodules for a given package"""
from os import path, walk
module = path.dirname(getattr(package, '__file__', package))
parent = path.dirname(module)
files = (path.join(dir, file)[len(parent) + 1:-3]
for dir, dirs, files in walk(module)
for file in files
if file.endswith('.py'))
files = (f for f in files if not f.startswith(SKIP_DIRS))
files = (f.replace(path.sep, '.') for f in files)
return files
class Context(dict):
"""
Execution context that *retains* the changes the tests make. Preferably
use one per module to obtain nice "literate" modules that "follow along".
In other words, directly the opposite of:
https://docs.python.org/3/library/doctest.html#what-s-the-execution-context
By popular demand:
http://stackoverflow.com/questions/13106118/object-reuse-in-python-doctest/13106793#13106793
http://stackoverflow.com/questions/3286658/embedding-test-code-or-data-within-doctest-strings
"""
def copy(self):
return self
def clear(self):
pass
def suite(package):
"""Assemble test suite for doctests in path (recusrively)"""
from importlib import import_module
for module in find_modules(package.__file__):
try:
module = import_module(module)
yield DocTestSuite(module
globs=Context(module.__dict__.copy()),
optionflags=ELLIPSIS | NORMALIZE_WHITESPACE)
except ValueError:
pass # No doctests in module
except ImportError:
import warnings
warnings.warn('Unimportable module: {}'.format(module))
def load_tests(loader, tests, ignore):
# This follows the load_tests protocol
# https://docs.python.org/3/library/unittest.html#load-tests-protocol
import MyPackage
tests.addTests(suite(MyPackage))
return tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment