-1

The unittest framework in python outputs a stack trace when you encounter an exception. Each line of the stack trace is accompanied by source code in the output. This causes the stack trace to be hundreds of lines of scrolling and, in my opinion, very hard to read.

Is there a way to run a python unittest so that when an exception is caught the stack trace output is in a shortened form? I'd like just the source lines, excluding all the extra source.

David Parks
  • 30,789
  • 47
  • 185
  • 328
  • Possible duplicate of [this one](https://stackoverflow.com/questions/33555891/make-python-unittest-show-assertionerror-but-no-traceback) – yorodm Jan 30 '19 at 21:57

1 Answers1

1

It is the job of TestResult.addError() to format exceptions:

Called when the test case test raises an unexpected exception. err is a tuple of the form returned by sys.exc_info(): (type, value, traceback).

The default implementation appends a tuple (test, formatted_err) to the instance’s errors attribute, where formatted_err is a formatted traceback derived from err.

If you want to format your tracebacks differently, this is where you'd step in to change them. You have two options:

  • Write your own traceback formatter (to replace the current implementation)
  • Post-process the formatted_err value that the base implementation adds.

The latter might be simpler; just delete any further indented lines following a line that starts with ' File':

import re
import unittest
from functools import partial

# traceback `File` lines are followed by an optional source line, and if
# locals are included, by <name> = <value> lines:
_tb_file_line = re.compile(
        r'(  File "[^"]*", line \d+, in [^\n]+\n)'
        r'    [^ ].*\n'
        r'(?:    \w+ = .*\n)*'
)
_clear_source_and_locals = partial(_tb_file_line.sub, r'\1')

class NoSourceTracebackResult(unittest.TextTestResult):
    def addError(self, test, err):
        super().addError(test, err)
        # remove error, format, then put it back again
        t, formatted_err = self.errors.pop()
        formatted_err = _clear_source_and_locals(formatted_err)
        self.errors.append((test, formatted_err))

class NoSourceTracebackRunner(unittest.TextTestRunner):
    resultclass = NoSourceTracebackResult

then use that as your test runner; if you run with unittest.main() in a __main__ guard, then use it like this:

if __name__ == '__main__':
    unittest.main(testRunner=NoSourceTracebackRunner)(
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343