Source code for pyquickhelper.pycode.pytest_helper

"""
:epkg:`pytest` is sometimes slow. This file provides helpers to
easily run test function.


:githublink:`%|py|6`
"""
import os
import importlib
import re
import warnings
import time
from traceback import format_exc
from inspect import signature


[docs]class TestExecutionError(RuntimeError): """ Raised when the execution of a test fails. :githublink:`%|py|18` """
[docs] def __init__(self, module, name, exc): """ :param module: module :param name: function name :param exc: exception :githublink:`%|py|25` """ if name is None and isinstance(exc, list): msg = "Test module '{}' failed\n---\n{}".format( module.__name__, "\n---\n".join(str(_) for _ in exc)) RuntimeError.__init__(self, msg) elif name is not None: msg = "Function '{}' from module '{}' failed due to '{}'\n{}".format( name, module.__name__, exc, format_exc()) RuntimeError.__init__(self, msg) else: raise RuntimeError( # pragma: no cover "Unknown test error.")
[docs]def run_test_function(module, pattern="^test_.*", stop_first=False, verbose=False, fLOG=print): """ Runs test functions from *module*. :param module: module (string or module) :param pattern: function pattern :param stop_first: stops at the first error or run all of them :param verbose: prints out the name of the functions :param fLOG: logging function The following piece of code could also be used to run all tests not using any parameter. :: fcts = [v for k, v in locals().items() if k.startswith('test_')] for fct in fcts: print("run", fct.__name__) try: fct() except Exception as e: if 'missing' in str(e): print(e) continue raise e :githublink:`%|py|64` """ if isinstance(module, str): module_path = module module = os.path.splitext(module)[0] _, module_name = os.path.split(os.path.splitext(module)[0]) with warnings.catch_warnings(record=False): spec = importlib.util.spec_from_file_location( module_name, module_path) if spec is None: raise ImportError( # pragma: no cover "Cannot import module '{}' from '{}'.".format( module_name, module_path)) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) if module is None: raise ValueError( # pragma: no cover "module must be specified.") reg = re.compile(pattern) fcts = [] for name, fct in module.__dict__.items(): if not reg.search(name): continue if not callable(fct): continue # pragma: no cover sig = signature(fct) if sig.parameters: continue fcts.append((name, fct)) excs = [] tested = [] i = 0 for name, fct in fcts: t0 = time.perf_counter() with warnings.catch_warnings(record=False): try: fct() exc = None except Exception as e: exc = TestExecutionError(module, name, e) if stop_first: raise exc excs.append(exc) dt = time.perf_counter() - t0 if verbose: fLOG( # pragma: no cover "[run_test_function] {}/{}: {} '{}' in {:0.000}s".format( i + 1, len(fcts), 'OK' if exc is None else '--', name, dt)) tested.append(name) i += 1 if len(excs) > 0: raise TestExecutionError(module, None, excs) if len(tested) == 0: raise ValueError( "No function found in '{}' with pattern '{}'.".format(module, pattern))