From 1780a5d1879bf74426cb26178441d6a55ed0d1e3 Mon Sep 17 00:00:00 2001 From: Dominik1123 <> Date: Tue, 22 Dec 2020 23:02:31 +0100 Subject: [PATCH 1/8] Add --debug command line option to unittest --- Lib/unittest/main.py | 14 +++++++++++--- Lib/unittest/runner.py | 8 +++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Lib/unittest/main.py b/Lib/unittest/main.py index e62469aa2a170f..e1a0323ca51c47 100644 --- a/Lib/unittest/main.py +++ b/Lib/unittest/main.py @@ -65,7 +65,7 @@ class TestProgram(object): def __init__(self, module='__main__', defaultTest=None, argv=None, testRunner=None, testLoader=loader.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, - buffer=None, warnings=None, *, tb_locals=False): + buffer=None, warnings=None, *, tb_locals=False, debug=False): if isinstance(module, str): self.module = __import__(module) for part in module.split('.')[1:]: @@ -81,6 +81,7 @@ def __init__(self, module='__main__', defaultTest=None, argv=None, self.verbosity = verbosity self.buffer = buffer self.tb_locals = tb_locals + self.debug = debug if warnings is None and not sys.warnoptions: # even if DeprecationWarnings are ignored by default # print them anyway unless other warnings settings are @@ -175,6 +176,8 @@ def _getParentArgParser(self): parser.add_argument('--locals', dest='tb_locals', action='store_true', help='Show local variables in tracebacks') + parser.add_argument('--debug', action='store_true', + help='Run the given tests in debug mode') if self.failfast is None: parser.add_argument('-f', '--failfast', dest='failfast', action='store_true', @@ -268,8 +271,13 @@ def runTests(self): else: # it is assumed to be a TestRunner instance testRunner = self.testRunner - self.result = testRunner.run(self.test) + if self.debug: + testRunner.debug(self.test) + success = True + else: + self.result = testRunner.run(self.test) + success = self.result.wasSuccessful() if self.exit: - sys.exit(not self.result.wasSuccessful()) + sys.exit(not success) main = TestProgram diff --git a/Lib/unittest/runner.py b/Lib/unittest/runner.py index 45e7e4c0458d4c..1a6869eb324a82 100644 --- a/Lib/unittest/runner.py +++ b/Lib/unittest/runner.py @@ -10,6 +10,12 @@ __unittest = True +class TestRunner: + def debug(self, test): + """Run the given test case or test suite in debug mode.""" + test.debug() + + class _WritelnDecorator(object): """Used to decorate file-like objects with a handy 'writeln' method""" def __init__(self,stream): @@ -117,7 +123,7 @@ def printErrorList(self, flavour, errors): self.stream.writeln("%s" % err) -class TextTestRunner(object): +class TextTestRunner(TestRunner): """A test runner class that displays results in textual form. It prints out the names of tests as they are run, errors as they From ef29ec5c7bdfb227aa55ca07f335f4b3bdc02f50 Mon Sep 17 00:00:00 2001 From: Dominik1123 <> Date: Tue, 22 Dec 2020 23:23:09 +0100 Subject: [PATCH 2/8] Add 'debug' option to unittests --- Lib/unittest/test/test_break.py | 1 + Lib/unittest/test/test_program.py | 1 + 2 files changed, 2 insertions(+) diff --git a/Lib/unittest/test/test_break.py b/Lib/unittest/test/test_break.py index eebd2b610ce11f..deb9dbe43d7424 100644 --- a/Lib/unittest/test/test_break.py +++ b/Lib/unittest/test/test_break.py @@ -207,6 +207,7 @@ def __init__(self, catchbreak): self.failfast = failfast self.catchbreak = catchbreak self.tb_locals = False + self.debug = False self.testRunner = FakeRunner self.test = test self.result = None diff --git a/Lib/unittest/test/test_program.py b/Lib/unittest/test/test_program.py index eef82ff937ab7c..caf61d3c34820e 100644 --- a/Lib/unittest/test/test_program.py +++ b/Lib/unittest/test/test_program.py @@ -136,6 +136,7 @@ class InitialisableProgram(unittest.TestProgram): verbosity = 1 defaultTest = None tb_locals = False + debug = False testRunner = None testLoader = unittest.defaultTestLoader module = '__main__' From be1e856bf0d8db932f46652f22cd1fa9b3d0d058 Mon Sep 17 00:00:00 2001 From: Dominik1123 <> Date: Tue, 22 Dec 2020 23:26:41 +0100 Subject: [PATCH 3/8] Move 'wasSuccessful' check where it was to not break unit tests --- Lib/unittest/main.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Lib/unittest/main.py b/Lib/unittest/main.py index e1a0323ca51c47..83df6dfafca665 100644 --- a/Lib/unittest/main.py +++ b/Lib/unittest/main.py @@ -273,11 +273,9 @@ def runTests(self): testRunner = self.testRunner if self.debug: testRunner.debug(self.test) - success = True else: self.result = testRunner.run(self.test) - success = self.result.wasSuccessful() if self.exit: - sys.exit(not success) + sys.exit(not (self.debug or self.result.wasSuccessful())) main = TestProgram From b43f4b1fd3f69ab252eed29aed32112717260606 Mon Sep 17 00:00:00 2001 From: Dominik1123 <> Date: Wed, 23 Dec 2020 00:09:19 +0100 Subject: [PATCH 4/8] Add unit tests for 'debug' command line option --- Lib/unittest/test/test_program.py | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Lib/unittest/test/test_program.py b/Lib/unittest/test/test_program.py index caf61d3c34820e..ce20106b0078b4 100644 --- a/Lib/unittest/test/test_program.py +++ b/Lib/unittest/test/test_program.py @@ -129,6 +129,37 @@ def test_ExitAsDefault(self): testRunner=unittest.TextTestRunner(stream=io.StringIO()), testLoader=self.FooBarLoader()) + class TestRaise(unittest.TestCase): + class Error(Exception): + pass + def test_raise(self): + raise self.Error + + class TestRaiseLoader(unittest.TestLoader): + def loadTestsFromModule(self, module): + return self.suiteClass( + [self.loadTestsFromTestCase(Test_TestProgram.TestRaise)]) + + def loadTestsFromNames(self, names, module): + return self.suiteClass( + [self.loadTestsFromTestCase(Test_TestProgram.TestRaise)]) + + def test_debug(self): + self.assertRaises( + self.TestRaise.Error, + unittest.main, + argv=["TestRaise", "--debug"], + testRunner=unittest.TextTestRunner(stream=io.StringIO()), + testLoader=self.TestRaiseLoader()) + + def test_no_debug(self): + self.assertRaises( + SystemExit, + unittest.main, + argv=["TestRaise"], + testRunner=unittest.TextTestRunner(stream=io.StringIO()), + testLoader=self.TestRaiseLoader()) + class InitialisableProgram(unittest.TestProgram): exit = False @@ -290,6 +321,12 @@ def test_locals(self): 'verbosity': 1, 'warnings': None}) + def test_debug(self): + program = self.program + program.testRunner = FakeRunner + program.parseArgs([None, '--debug']) + self.assertTrue(program.debug) + def testRunTestsOldRunnerClass(self): program = self.program From 78a4e32d6fe52f6cfe70630692f18d51d79ba3f2 Mon Sep 17 00:00:00 2001 From: Dominik1123 <> Date: Wed, 23 Dec 2020 00:20:52 +0100 Subject: [PATCH 5/8] Update docs: add command line option '--debug' for unittest --- Doc/library/unittest.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 0a0993518efddc..784fde7d065f2e 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -215,6 +215,11 @@ Command-line options See `Signal Handling`_ for the functions that provide this functionality. +.. cmdoption:: --debug + + Run test cases in :meth:`~TestCase.debug` mode. + This is useful for post-mortem debugging via :mod:`pdb`. + .. cmdoption:: -f, --failfast Stop the test run on the first error or failure. @@ -248,6 +253,9 @@ Command-line options .. versionadded:: 3.7 The command-line option ``-k``. +.. versionadded:: 3.11 + The command-line option ``--debug``. + The command line can also be used for test discovery, for running all of the tests in a project or just a subset. From fcf1a93e0dfecd1720b773b3177306d2f0f8cea7 Mon Sep 17 00:00:00 2001 From: Dominik1123 <15989985+Dominik1123@users.noreply.github.com> Date: Thu, 24 Dec 2020 02:16:00 +0100 Subject: [PATCH 6/8] Use versionadded 3.10 Co-authored-by: Zackery Spytz --- Doc/library/unittest.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 784fde7d065f2e..952148c1e22bf8 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -253,7 +253,7 @@ Command-line options .. versionadded:: 3.7 The command-line option ``-k``. -.. versionadded:: 3.11 +.. versionadded:: 3.10 The command-line option ``--debug``. The command line can also be used for test discovery, for running all of the From c20eb768ee5b8c8720e61425297d6a331bde0303 Mon Sep 17 00:00:00 2001 From: Dominik1123 <> Date: Thu, 24 Dec 2020 02:18:51 +0100 Subject: [PATCH 7/8] Limit to 79 characters per line --- Lib/unittest/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/unittest/main.py b/Lib/unittest/main.py index 83df6dfafca665..55bf6caa52832c 100644 --- a/Lib/unittest/main.py +++ b/Lib/unittest/main.py @@ -65,7 +65,8 @@ class TestProgram(object): def __init__(self, module='__main__', defaultTest=None, argv=None, testRunner=None, testLoader=loader.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, - buffer=None, warnings=None, *, tb_locals=False, debug=False): + buffer=None, warnings=None, *, tb_locals=False, + debug=False): if isinstance(module, str): self.module = __import__(module) for part in module.split('.')[1:]: From 8927d391f6e6b97ec163feb11b7a2d5a73722b62 Mon Sep 17 00:00:00 2001 From: Dominik1123 <> Date: Thu, 24 Dec 2020 02:27:07 +0100 Subject: [PATCH 8/8] Add news file --- .../NEWS.d/next/Library/2020-12-24-02-26-25.bpo-42722.ZNAp9W.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2020-12-24-02-26-25.bpo-42722.ZNAp9W.rst diff --git a/Misc/NEWS.d/next/Library/2020-12-24-02-26-25.bpo-42722.ZNAp9W.rst b/Misc/NEWS.d/next/Library/2020-12-24-02-26-25.bpo-42722.ZNAp9W.rst new file mode 100644 index 00000000000000..02b7dd623a8e0e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-12-24-02-26-25.bpo-42722.ZNAp9W.rst @@ -0,0 +1 @@ +Added `--debug` command line option to :mod:`unittest`.