diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 6f783bb7ef..26d10988d9 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -752,6 +752,7 @@ def transform_database(context, table, engine, row_format, failfast): ) @click.option("--test", multiple=True, help="Specific test") @click.option("--module", help="Run tests in a module") +@click.option("--pdb", is_flag=True, default=False, help="Open pdb on AssertionError") @click.option("--profile", is_flag=True, default=False) @click.option("--coverage", is_flag=True, default=False) @click.option("--skip-test-records", is_flag=True, default=False, help="Don't create test records") @@ -776,9 +777,14 @@ def run_tests( skip_before_tests=False, failfast=False, case=None, + pdb=False, ): """Run python unit-tests""" + pdb_on_exceptions = None + if pdb: + pdb_on_exceptions = (AssertionError,) + with CodeCoverage(coverage, app): import frappe import frappe.test_runner @@ -810,6 +816,7 @@ def run_tests( case=case, skip_test_records=skip_test_records, skip_before_tests=skip_before_tests, + pdb_on_exceptions=pdb_on_exceptions, ) if len(ret.failures) == 0 and len(ret.errors) == 0: diff --git a/frappe/test_runner.py b/frappe/test_runner.py index ccbec643eb..1c40d48232 100644 --- a/frappe/test_runner.py +++ b/frappe/test_runner.py @@ -53,6 +53,7 @@ def main( case=None, skip_test_records=False, skip_before_tests=False, + pdb_on_exceptions=False, ): global unittest_runner @@ -78,6 +79,7 @@ def main( try: frappe.flags.print_messages = verbose frappe.flags.in_test = True + frappe.flags.pdb_on_exceptions = pdb_on_exceptions # workaround! since there is no separate test db frappe.clear_cache() @@ -290,6 +292,11 @@ def _run_unittest( else: final_test_suite.addTest(test_suite) + if frappe.flags.pdb_on_exceptions: + for test_case in iterate_suite(final_test_suite): + if hasattr(test_case, "_apply_debug_decorator"): + test_case._apply_debug_decorator(frappe.flags.pdb_on_exceptions) + if junit_xml_output: runner = unittest_runner(verbosity=1 + cint(verbose), failfast=failfast) else: diff --git a/frappe/tests/utils.py b/frappe/tests/utils.py index d62474ec4f..7294498d33 100644 --- a/frappe/tests/utils.py +++ b/frappe/tests/utils.py @@ -105,24 +105,10 @@ class FrappeTestCase(unittest.TestCase): cls.addClassCleanup(_restore_thread_locals, copy.deepcopy(frappe.local.flags)) cls.addClassCleanup(_rollback_db) - cls._apply_debug_decorator() - return super().setUpClass() - @classmethod - def _apply_debug_decorator(cls): - import sys - - pdb_flag = next((arg for arg in sys.argv if arg.startswith('--pdb')), None) - if pdb_flag: - exceptions = (AssertionError,) - if pdb_flag.startswith('--pdb-on='): - exception_names = pdb_flag.split('=')[1].split(',') - exceptions = tuple(getattr(__builtins__, name.strip()) for name in exception_names) - - for attr in dir(cls): - if attr.startswith('test_'): - setattr(cls, attr, debug_on(*exceptions)(getattr(cls, attr))) + def _apply_debug_decorator(self, exceptions=()): + setattr(self, self._testMethodName, debug_on(*exceptions)(getattr(self, self._testMethodName))) def assertSequenceSubset(self, larger: Sequence, smaller: Sequence, msg=None): """Assert that `expected` is a subset of `actual`."""