1

I am trying to implement a clean answer for Continuing in Python's unittest when an assertion fails.

I would like the improve the most voted and non-accepted answer: https://stackoverflow.com/a/5028110/4934640

The problem I have this answer, is that it forces-me to use try...catch inside my Unit Tests. Then, I am trying to encapsulate the try...catch logic in an override of the TestCase.assertEqual method.

So far, so good. I manage to override the TestCase.assertEqual method, however, the stacktrace presented is wrong.

Running the following minimal example:

import unittest
import traceback

class MultipleAssertionFailures(unittest.TestCase):

    def __init__(self, *args, **kwargs):
        self.verificationErrors = []
        super(MultipleAssertionFailures, self).__init__( *args, **kwargs )

    def tearDown(self):
        super(MultipleAssertionFailures, self).tearDown()

        if self.verificationErrors:
            self.fail( '\n\n' + '\n'.join( self.verificationErrors ) )
            self.verificationErrors.clear()

    def assertEqual(self, goal, results, msg=""):

        try:
            super( MultipleAssertionFailures, self ).assertEqual( goal, results, msg )

        except unittest.TestCase.failureException as error:
            stacktrace = traceback.format_exc()
            # stacktrace = traceback.format_stack()
            self.verificationErrors.append( "".join( stacktrace ) )

# class DummyTestCase(unittest.TestCase):
class DummyTestCase(MultipleAssertionFailures):

    def setUp(self):
        self.maxDiff = None
        super(DummyTestCase, self).setUp()

    def tearDown(self):
        super(DummyTestCase, self).tearDown()

    def test_function_name(self):
        self.assertEqual( "var", "bar" )
        self.assertEqual( "1937", "511" )

if __name__ == '__main__':
    unittest.main()

You got the following output:

F
======================================================================
FAIL: test_function_name (__main__.DummyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\User\Downloads\test.py", line 36, in tearDown
    super(DummyTestCase, self).tearDown()
  File "D:\User\Downloads\test.py", line 15, in tearDown
    self.fail( '\n\n' + '\n'.join( self.verificationErrors ) )
AssertionError: 

Traceback (most recent call last):
  File "D:\User\Downloads\test.py", line 21, in assertEqual
    super( MultipleAssertionFailures, self ).assertEqual( goal, results, msg )
  File "F:\Python\lib\unittest\case.py", line 844, in assertEqual
    assertion_func(first, second, msg=msg)
  File "F:\Python\lib\unittest\case.py", line 1228, in assertMultiLineEqual
    self.fail(self._formatMessage(msg, standardMsg))
  File "F:\Python\lib\unittest\case.py", line 685, in fail
    raise self.failureException(msg)
AssertionError: 'var' != 'bar'
- var
? ^
+ bar
? ^
 : 

Traceback (most recent call last):
  File "D:\User\Downloads\test.py", line 21, in assertEqual
    super( MultipleAssertionFailures, self ).assertEqual( goal, results, msg )
  File "F:\Python\lib\unittest\case.py", line 844, in assertEqual
    assertion_func(first, second, msg=msg)
  File "F:\Python\lib\unittest\case.py", line 1228, in assertMultiLineEqual
    self.fail(self._formatMessage(msg, standardMsg))
  File "F:\Python\lib\unittest\case.py", line 685, in fail
    raise self.failureException(msg)
AssertionError: '1937' != '511'
- 1937
+ 511
 : 

On the output, you will noticed the two presented stacktraces are wrong because they do not reflect where the Unit Tests code are, but only where the my assertion code is on. By commenting the class DummyTestCase(MultipleAssertionFailures) and uncommenting the class DummyTestCase(unittest.TestCase) on the minimal example, you will see how the correctly output of the stacktrace should be:

F
======================================================================
FAIL: test_function_name (__main__.DummyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\User\Downloads\test.py", line 39, in test_function_name
    self.assertEqual( "var", "bar" )
AssertionError: 'var' != 'bar'
- var
? ^
+ bar
? ^

This stacktrace (the builtin one), correctly points to the test.py", line 39, in test_function_name. I tried looking over the TestCase module, but I could not figure out how it did to manage to produce this meaningful stacktrace.

Alternatively, uncommenting the line stacktrace = traceback.format_exc(), we can get a really full stacktrace:

F
======================================================================
FAIL: test_function_name (__main__.DummyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\User\Downloads\test.py", line 36, in tearDown
    super(DummyTestCase, self).tearDown()
  File "D:\User\Downloads\test.py", line 15, in tearDown
    self.fail( '\n\n' + '\n'.join( self.verificationErrors ) )
AssertionError: 

  File "D:\User\Downloads\test.py", line 43, in <module>
    unittest.main()
  File "F:\Python\lib\unittest\main.py", line 101, in __init__
    self.runTests()
  File "F:\Python\lib\unittest\main.py", line 271, in runTests
    self.result = testRunner.run(self.test)
  File "F:\Python\lib\unittest\runner.py", line 176, in run
    test(result)
  File "F:\Python\lib\unittest\suite.py", line 84, in __call__
    return self.run(*args, **kwds)
  File "F:\Python\lib\unittest\suite.py", line 122, in run
    test(result)
  File "F:\Python\lib\unittest\suite.py", line 84, in __call__
    return self.run(*args, **kwds)
  File "F:\Python\lib\unittest\suite.py", line 122, in run
    test(result)
  File "F:\Python\lib\unittest\case.py", line 668, in __call__
    return self.run(*args, **kwds)
  File "F:\Python\lib\unittest\case.py", line 620, in run
    testMethod()
  File "D:\User\Downloads\test.py", line 39, in test_function_name
    self.assertEqual( "var", "bar" )
  File "D:\User\Downloads\test.py", line 25, in assertEqual
    stacktrace = traceback.format_stack()

  File "D:\User\Downloads\test.py", line 43, in <module>
    unittest.main()
  File "F:\Python\lib\unittest\main.py", line 101, in __init__
    self.runTests()
  File "F:\Python\lib\unittest\main.py", line 271, in runTests
    self.result = testRunner.run(self.test)
  File "F:\Python\lib\unittest\runner.py", line 176, in run
    test(result)
  File "F:\Python\lib\unittest\suite.py", line 84, in __call__
    return self.run(*args, **kwds)
  File "F:\Python\lib\unittest\suite.py", line 122, in run
    test(result)
  File "F:\Python\lib\unittest\suite.py", line 84, in __call__
    return self.run(*args, **kwds)
  File "F:\Python\lib\unittest\suite.py", line 122, in run
    test(result)
  File "F:\Python\lib\unittest\case.py", line 668, in __call__
    return self.run(*args, **kwds)
  File "F:\Python\lib\unittest\case.py", line 620, in run
    testMethod()
  File "D:\User\Downloads\test.py", line 40, in test_function_name
    self.assertEqual( "1937", "511" )
  File "D:\User\Downloads\test.py", line 25, in assertEqual
    stacktrace = traceback.format_stack()

How could I always cut off this full stacktrace into something meaningful as the builtin TestCase.assertEqual does?

Evandro Coan
  • 8,560
  • 11
  • 83
  • 144
  • I just posted some sort of [answer](https://stackoverflow.com/a/54499865/4934640) I manage to build for this over the original question. – Evandro Coan Feb 03 '19 at 04:17

0 Answers0