Instantly share code, notes, and snippets.

Embed
What would you like to do?
Check eval(repr(cls())) == cls() for whole package
#!/usr/bin/env python3
# vim: set fenc=utf8 ts=4 sw=4 et :
"""
Tests the follwoing rules for a whole package:
1. Repr is unique: All classes must implement __repr__ with eval(repr(cls())) == cls()
"""
import unittest
import sys
import inspect
import pkgutil
import importlib
# modules under test
MODULE_NAMES_TO_TEST=['pgc']
class CodeQuality(unittest.TestCase):
# empty for now, add test during run time
pass
def _import_submodules(package, recursive=True):
""" Import all submodules of a module, recursively, including subpackages
:param package: package (name or actual module)
:type package: str | module
:rtype: dict[str, types.ModuleType]
"""
if isinstance(package, str):
package = importlib.import_module(package)
results = {}
for loader, name, is_pkg in pkgutil.walk_packages(package.__path__):
full_name = package.__name__ + '.' + name
results[full_name] = importlib.import_module(full_name)
if recursive and is_pkg:
results.update(_import_submodules(full_name))
return results
def _test_all_classes(package, make_test):
"""Adds test for all classes in package."""
for module_name, module in _import_submodules(package).items():
for cls_name, cls in inspect.getmembers(
module,
lambda x: inspect.isclass(x) and x.__module__ == module_name # is class and define in module
):
test = make_test(cls)
test_name = 'test_{}_{}_{}'.format(
module_name,
cls_name,
test.__name__
)
setattr(
CodeQuality,
test_name,
test
)
def _make_repr_test(cls):
""" Returns test which cheks repr for uniqueness."""
def _repr(self):
# can eval?
try:
eval(repr(cls())),
except Exception as e:
raise AssertionError('eval(repr({}()))'.format(
cls.__name__
)) from e
# is repr unambiguous?
self.assertEqual(
eval(repr(cls())),
cls()
)
return _repr
def _add_tests_for_modules(module_names_to_test):
for module_name in module_names_to_test:
module = importlib.import_module(module_name)
globals()[module_name] = module
# add code quality tests
_test_all_classes(module, _make_repr_test)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(description='Unit test code quality')
parser.add_argument(
'modules',
metavar='module',
type=str,
nargs='*',
help='module to test',
default=[]
)
args = parser.parse_args()
_add_tests_for_modules(args.modules)
unittest.main(argv=[sys.argv[0]]) # hide command line arguments
else:
_add_tests_for_modules(MODULE_NAMES_TO_TEST)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment